<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="IO&amp;NIO, 博客 blog">
    <meta name="description" content="熊猫小二的博客  xmxe&#39;s blog">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <!-- 为了引用qq空间图床文件 -->
    <meta name="referrer" content="no-referrer">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    <title>IO&amp;NIO | 熊猫小二</title>
    <link rel="icon" type="image/x-icon, image/vnd.microsoft.icon" href="/blog/favicon.ico">
    <link rel="stylesheet" type="text/css" href="/blog/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/blog/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/blog/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/blog/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/blog/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/blog/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/blog/css/my.css">
    <link rel="stylesheet" type="text/css" href="/blog/css/loading.css">

    <script src="/blog/libs/jquery/jquery.min.js"></script>

<meta name="generator" content="Hexo 6.3.0"></head>



   
<style>
    body{
       background-image: url(/blog/medias/cover.jpg);
       background-repeat:no-repeat;
       background-size:cover;
       background-attachment:fixed;
    }
</style>



<body>
    
  <div id="loading-box">
    <div class="loading-left-bg"></div>
    <div class="loading-right-bg"></div>
    <div class="spinner-box">
      <div class="configure-border-1">
        <div class="configure-core"></div>
      </div>
      <div class="configure-border-2">
        <div class="configure-core"></div>
      </div>
      <div class="loading-word">加载中...</div>
    </div>
  </div>
  <!-- 页面加载动画 -->
  <script>
    $(document).ready(function () {
      // document.body.style.overflow = 'auto';
      document.getElementById('loading-box').classList.add("loaded")
    })
  </script>

    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/blog/" class="waves-effect waves-light">
                    
                        <img src="/blog/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">熊猫小二</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/blog/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="#" class="waves-effect waves-light">

      
      <i class="fas fa-book" style="zoom: 0.6;"></i>
      
      <span>归档</span>
      <i class="fas fa-chevron-down" aria-hidden="true" style="zoom: 0.6;"></i>
    </a>
    <ul class="sub-nav menus_item_child ">
      
      <li>
        <a href="/blog/archives">
          
          <i class="fas fa-archive" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>归档</span>
        </a>
      </li>
      
      <li>
        <a href="/blog/tags">
          
          <i class="fas fa-tags" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>标签</span>
        </a>
      </li>
      
      <li>
        <a href="/blog/categories">
          
          <i class="fas fa-bookmark" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>分类</span>
        </a>
      </li>
      
    </ul>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/blog/friends" class="waves-effect waves-light">
      
      <i class="fas fa-address-book" style="zoom: 0.6;"></i>
      
      <span>友链</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="#" class="waves-effect waves-light">

      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
      <i class="fas fa-chevron-down" aria-hidden="true" style="zoom: 0.6;"></i>
    </a>
    <ul class="sub-nav menus_item_child ">
      
      <li>
        <a href="/blog/about">
          
          <i class="fas fa-star-of-david" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>主页</span>
        </a>
      </li>
      
      <li>
        <a href="/blog/gallery">
          
          <i class="fas fa-images" style="margin-top: -20px; zoom: 0.6;"></i>
          
          <span>相册</span>
        </a>
      </li>
      
    </ul>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
  
    <li>
      <a class="waves-effect waves-light" onclick="switchNightMode()">
        <i id="sum-moon-icon" class="fas fa-sun" style="zoom:0.65;" title="切换主题"></i>
      </a>
    </li>
  
  
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
          <img src="/blog/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">熊猫小二</div>
        <div class="logo-desc">
            
            熊猫小二的博客  xmxe&#39;s blog
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/blog/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="javascript:;">
			
				<i class="fa-fw fas fa-book"></i>
			
			归档
			<span class="m-icon"><i class="fas fa-chevron-right"></i></span>
		</a>
            <ul  >
              
                <li>

                  <a href="/blog/archives " style="margin-left:75px">
				  
				   <i class="fa fas fa-archive" style="position: absolute;left:50px" ></i>
			      
		          <span>归档</span>
                  </a>
                </li>
              
                <li>

                  <a href="/blog/tags " style="margin-left:75px">
				  
				   <i class="fa fas fa-tags" style="position: absolute;left:50px" ></i>
			      
		          <span>标签</span>
                  </a>
                </li>
              
                <li>

                  <a href="/blog/categories " style="margin-left:75px">
				  
				   <i class="fa fas fa-bookmark" style="position: absolute;left:50px" ></i>
			      
		          <span>分类</span>
                  </a>
                </li>
              
            </ul>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/blog/friends" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-address-book"></i>
			
			友链
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="javascript:;">
			
				<i class="fa-fw fas fa-user-circle"></i>
			
			关于
			<span class="m-icon"><i class="fas fa-chevron-right"></i></span>
		</a>
            <ul  >
              
                <li>

                  <a href="/blog/about " style="margin-left:75px">
				  
				   <i class="fa fas fa-star-of-david" style="position: absolute;left:50px" ></i>
			      
		          <span>主页</span>
                  </a>
                </li>
              
                <li>

                  <a href="/blog/gallery " style="margin-left:75px">
				  
				   <i class="fa fas fa-images" style="position: absolute;left:50px" ></i>
			      
		          <span>相册</span>
                  </a>
                </li>
              
            </ul>
          
        </li>
        
        
    </ul>
</div>


        </div>

        
    </nav>

</header>

    
<script src="/blog/libs/cryptojs/crypto-js.min.js"></script>
<script>
    (function() {
        let pwd = '';
        if (pwd && pwd.length > 0) {
            if (pwd !== CryptoJS.SHA256(prompt('请输入访问本文章的密码')).toString(CryptoJS.enc.Hex)) {
                alert('密码错误，将返回主页！');
                location.href = '/blog/';
            }
        }
    })();
</script>




<div class="bg-cover pd-header post-cover" style="background-image: url('/blog/medias/featureimages/1.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 id="post-title" class="description center-align post-title"></h1>

                    
                        <!-- <script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11"></script> -->
                        <script>
                            var typedObj = new Typed("#post-title", {
                                strings: [ 'IO&amp;NIO' ],
                                startDelay: 300,
                                typeSpeed: 70,
                                loop: false,
                                backSpeed: 50,
                                showCursor: true
                            });
                        </script>
                    
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/blog/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
        background-color: rgb(255, 255, 255,0.7);
        border-radius: 10px;
        box-shadow: 0 10px 35px 2px rgba(0, 0, 0, .15), 0 5px 15px rgba(0, 0, 0, .07), 0 2px 5px -5px rgba(0, 0, 0, .1) !important;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
        max-height: 480px;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                          <div class="article-tag">
                            <span class="chip bg-color">无标签</span>
                          </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/blog/categories/Java/" class="post-category">
                                Java
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                

                

                

                

                
            </div>
        </div>
        <hr class="clearfix">

        
        <!-- 是否加载使用自带的 prismjs. -->
        <link rel="stylesheet" href="/blog/libs/prism/prism.css">
        

        
        <!-- 代码块折行 -->
        <style type="text/css">
            code[class*="language-"], pre[class*="language-"] { white-space: pre-wrap !important; }
        </style>
        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <h2 id="IO"><a href="#IO" class="headerlink" title="IO"></a>IO</h2><h3 id="IO基础知识总结"><a href="#IO基础知识总结" class="headerlink" title="IO基础知识总结"></a>IO基础知识总结</h3><h4 id="IO流简介"><a href="#IO流简介" class="headerlink" title="IO流简介"></a>IO流简介</h4><p>IO即<strong>Input&#x2F;Output</strong>，输入和输出。数据输入到计算机内存的过程即输入，反之输出到外部存储（比如数据库，文件，远程主机）的过程即输出。数据传输过程类似于水流，因此称为IO流。IO流在Java中分为输入流和输出流，而根据数据的处理方式又分为字节流和字符流。</p>
<p>Java IO流的40多个类都是从如下4个抽象类基类中派生出来的。</p>
<ul>
<li><strong>InputStream&#x2F;Reader</strong>：所有的输入流的基类，前者是字节输入流，后者是字符输入流。</li>
<li><strong>OutputStream&#x2F;Writer</strong>：所有输出流的基类，前者是字节输出流，后者是字符输出流。</li>
</ul>
<h4 id="字节流"><a href="#字节流" class="headerlink" title="字节流"></a>字节流</h4><p><strong>InputStream（字节输入流）</strong></p>
<p>InputStream用于从源头（通常是文件）读取数据（字节信息）到内存中，java.io.InputStream抽象类是所有字节输入流的父类。InputStream常用方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 返回输入流中下一个字节的数据。返回的值介于0到255之间。如果未读取任何字节，则代码返回-1，表示文件结束。</span>
<span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 从输入流中读取一些字节存储到数组b中。如果数组b的长度为零，则不读取。如果没有可用字节读取，返回-1。如果有可用字节读取，则最多读取的字节数最多等于b.length，返回读取的字节数。这个方法等价于read(b,0,b.length)。</span>
<span class="token function">read</span><span class="token punctuation">(</span><span class="token keyword">byte</span> b<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token comment">// 在read(byte b[])方法的基础上增加了off参数（偏移量）和len参数（要读取的最大字节数）</span>
<span class="token function">read</span><span class="token punctuation">(</span><span class="token keyword">byte</span> b<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">int</span> off<span class="token punctuation">,</span><span class="token keyword">int</span> len<span class="token punctuation">)</span>
<span class="token comment">// 忽略输入流中的n个字节,返回实际忽略的字节数。</span>
<span class="token function">skip</span><span class="token punctuation">(</span><span class="token keyword">long</span> n<span class="token punctuation">)</span>
<span class="token comment">// 返回输入流中可以读取的字节数。</span>
<span class="token function">available</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 关闭输入流释放相关的系统资源。</span>
<span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token comment">// 从Java9开始，InputStream新增加了多个实用的方法：</span>
<span class="token comment">// 读取输入流中的所有字节，返回字节数组。</span>
<span class="token function">readAllBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 阻塞直到读取len个字节。</span>
<span class="token function">readNBytes</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span>b<span class="token punctuation">,</span>intoff<span class="token punctuation">,</span>intlen<span class="token punctuation">)</span>
<span class="token comment">// 将所有字节从一个输入流传递到一个输出流。</span>
<span class="token function">transferTo</span><span class="token punctuation">(</span><span class="token class-name">OutputStreamout</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>FileInputStream</strong>是一个比较常用的字节输入流对象，可直接指定文件路径，可以直接读取单字节数据，也可以读取至字节数组中。</p>
<p>FileInputStream代码示例：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">InputStream</span> fis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Number of remaining bytes:"</span>
            <span class="token operator">+</span> fis<span class="token punctuation">.</span><span class="token function">available</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">int</span> content<span class="token punctuation">;</span>
    <span class="token keyword">long</span> skip <span class="token operator">=</span> fis<span class="token punctuation">.</span><span class="token function">skip</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"The actual number of bytes skipped:"</span> <span class="token operator">+</span> skip<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"The content read from file:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>content <span class="token operator">=</span> fis<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>input.txt文件内容：</p>
<p><img src="https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/image-20220419155214614.png" alt="img"></p>
<p>输出：</p>
<pre class="line-numbers language-text" data-language="text"><code class="language-text">Number of remaining bytes:11
The actual number of bytes skipped:2
The content read from file:JavaGuide<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>

<p>不过，一般我们是不会直接单独使用FileInputStream，通常会配合BufferedInputStream（字节缓冲输入流，后文会讲到）来使用。像下面这段代码在我们的项目中就比较常见，我们通过readAllBytes()读取输入流所有字节并将其直接赋值给一个String对象。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 新建一个BufferedInputStream对象</span>
<span class="token class-name">BufferedInputStream</span> bufferedInputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 读取文件的内容并复制到String对象中</span>
<span class="token class-name">String</span> result <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span>bufferedInputStream<span class="token punctuation">.</span><span class="token function">readAllBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p><strong>DataInputStream</strong>用于读取指定类型数据，不能单独使用，必须结合FileInputStream。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">FileInputStream</span> fileInputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//必须将fileInputStream作为构造参数才能使用</span>
<span class="token class-name">DataInputStream</span> dataInputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DataInputStream</span><span class="token punctuation">(</span>fileInputStream<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//可以读取任意具体的类型数据</span>
dataInputStream<span class="token punctuation">.</span><span class="token function">readBoolean</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
dataInputStream<span class="token punctuation">.</span><span class="token function">readInt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
dataInputStream<span class="token punctuation">.</span><span class="token function">readUTF</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>ObjectInputStream</strong>用于从输入流中读取Java对象（反序列化），ObjectOutputStream用于将对象写入到输出流(序列化)。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">ObjectInputStream</span> input <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ObjectInputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"object.data"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">MyClass</span> object <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">MyClass</span><span class="token punctuation">)</span> input<span class="token punctuation">.</span><span class="token function">readObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
input<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>

<p>另外，用于序列化和反序列化的类必须实现Serializable接口，对象中如果有属性不想被序列化，使用transient修饰。</p>
<p><strong>OutputStream（字节输出流）</strong></p>
<p>OutputStream用于将数据（字节信息）写入到目的地（通常是文件），java.io.OutputStream抽象类是所有字节输出流的父类。OutputStream常用方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 将特定字节写入输出流。</span>
<span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">int</span> b<span class="token punctuation">)</span>
<span class="token comment">// 将数组b写入到输出流，等价于write(b, 0, b.length)。</span>
<span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">byte</span> b<span class="token punctuation">[</span> <span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token comment">// 在write(byte b[ ])方法的基础上增加了off参数（偏移量）和len参数（要读取的最大字节数）。</span>
<span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> b<span class="token punctuation">,</span> <span class="token keyword">int</span> off<span class="token punctuation">,</span> <span class="token keyword">int</span> len<span class="token punctuation">)</span>
<span class="token comment">// 刷新此输出流并强制写出所有缓冲的输出字节。</span>
<span class="token function">flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 关闭输出流释放相关的系统资源。</span>
<span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>FileOutputStream</strong>是最常用的字节输出流对象，可直接指定文件路径，可以直接输出单字节数据，也可以输出指定的字节数组。FileOutputStream代码示例：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">FileOutputStream</span> output <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"output.txt"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> array <span class="token operator">=</span> <span class="token string">"JavaGuide"</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    output<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>array<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>运行结果：</p>
<p><img src="https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/image-20220419155514392.png" alt="img"></p>
<p>类似于FileInputStream,FileOutputStream通常也会配合BufferedOutputStream（字节缓冲输出流，后文会讲到）来使用。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">FileOutputStream</span> fileOutputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"output.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">BufferedOutputStream</span> bos <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedOutputStream</span><span class="token punctuation">(</span>fileOutputStream<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>

<p><strong>DataOutputStream</strong>用于写入指定类型数据，不能单独使用，必须结合FileOutputStream</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 输出流</span>
<span class="token class-name">FileOutputStream</span> fileOutputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"out.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">DataOutputStream</span> dataOutputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DataOutputStream</span><span class="token punctuation">(</span>fileOutputStream<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 输出任意数据类型</span>
dataOutputStream<span class="token punctuation">.</span><span class="token function">writeBoolean</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
dataOutputStream<span class="token punctuation">.</span><span class="token function">writeByte</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>ObjectInputStream</strong>用于从输入流中读取Java对象（ObjectInputStream,反序列化），ObjectOutputStream将对象写入到输出流(ObjectOutputStream，序列化)。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">ObjectOutputStream</span> output <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ObjectOutputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"file.txt"</span><span class="token punctuation">)</span>
<span class="token class-name">Person</span> person <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token string">"Guide哥"</span><span class="token punctuation">,</span><span class="token string">"JavaGuide作者"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
output<span class="token punctuation">.</span><span class="token function">writeObject</span><span class="token punctuation">(</span>person<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>

<h4 id="字符流"><a href="#字符流" class="headerlink" title="字符流"></a>字符流</h4><p>不管是文件读写还是网络发送接收，信息的最小存储单元都是字节。<strong>那为什么I&#x2F;O流操作要分为字节流操作和字符流操作呢</strong>？个人认为主要有两点原因：</p>
<ul>
<li>字符流是由Java虚拟机将字节转换得到的，这个过程还算是比较耗时。</li>
<li>如果我们不知道编码类型就很容易出现乱码问题。</li>
</ul>
<p>乱码问题这个很容易就可以复现，我们只需要将上面提到的FileInputStream代码示例中的input.txt文件内容改为中文即可，原代码不需要改动。</p>
<p><img src="https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/image-20220419154632551.png" alt="img"></p>
<p>输出：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Number</span> of remaining bytes<span class="token operator">:</span><span class="token number">9</span>
<span class="token class-name">The</span> actual number of bytes skipped<span class="token operator">:</span><span class="token number">2</span>
<span class="token class-name">The</span> content read from file<span class="token operator">:</span>§å®¶å¥½<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>

<p>可以很明显地看到读取出来的内容已经变成了乱码。因此，I&#x2F;O流就干脆提供了一个直接操作字符的接口，方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好，如果涉及到字符的话使用字符流比较好。字符流默认采用的是Unicode编码，我们可以通过构造方法自定义编码。顺便分享一下之前遇到的笔试题：常用字符编码所占字节数？utf8:英文占1字节，中文占3字节，unicode：任何字符都占2个字节，gbk：英文占1字节，中文占2字节。</p>
<p><strong>Reader（字符输入流）</strong></p>
<p>Reader用于从源头（通常是文件）读取数据（字符信息）到内存中，java.io.Reader抽象类是所有字符输入流的父类。</p>
<p>Reader用于读取文本，InputStream用于读取原始字节。Reader常用方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 从输入流读取一个字符。</span>
<span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 从输入流中读取一些字符，并将它们存储到字符数组cbuf中，等价于read(cbuf, 0, cbuf.length)。</span>
<span class="token function">read</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">[</span><span class="token punctuation">]</span> cbuf<span class="token punctuation">)</span>
<span class="token comment">// 在read(char[] cbuf)方法的基础上增加了off参数（偏移量）和len参数（要读取的最大字符数）</span>
<span class="token function">read</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">[</span><span class="token punctuation">]</span> cbuf<span class="token punctuation">,</span> <span class="token keyword">int</span> off<span class="token punctuation">,</span> <span class="token keyword">int</span> len<span class="token punctuation">)</span>
<span class="token comment">// 忽略输入流中的n个字符,返回实际忽略的字符数。</span>
<span class="token function">skip</span><span class="token punctuation">(</span><span class="token keyword">long</span> n<span class="token punctuation">)</span>
<span class="token comment">// 关闭输入流并释放相关的系统资源。</span>
<span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>InputStreamReader</strong>是字节流转换为字符流的桥梁，其子类FileReader是基于该基础上的封装，可以直接操作字符文件。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 字节流转换为字符流的桥梁</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">InputStreamReader</span> <span class="token keyword">extends</span> <span class="token class-name">Reader</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span>
<span class="token comment">// 用于读取字符文件</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FileReader</span> <span class="token keyword">extends</span> <span class="token class-name">InputStreamReader</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>FileReader</strong>代码示例：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">FileReader</span> fileReader <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileReader</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">int</span> content<span class="token punctuation">;</span>
    <span class="token keyword">long</span> skip <span class="token operator">=</span> fileReader<span class="token punctuation">.</span><span class="token function">skip</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"The actual number of bytes skipped:"</span> <span class="token operator">+</span> skip<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"The content read from file:"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>content <span class="token operator">=</span> fileReader<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>input.txt文件内容：</p>
<p><img src="https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/image-20220419154632551.png" alt="img"></p>
<p>输出：</p>
<pre class="line-numbers language-text" data-language="text"><code class="language-text">The actual number of bytes skipped:3
The content read from file:我是Guide。<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>

<p><strong>Writer（字符输出流）</strong></p>
<p>Writer用于将数据（字符信息）写入到目的地（通常是文件），java.io.Writer抽象类是所有字符输出流的父类。Writer常用方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 写入单个字符。</span>
<span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">int</span> c<span class="token punctuation">)</span>
<span class="token comment">// 写入字符数组cbuf，等价于write(cbuf, 0, cbuf.length)。</span>
<span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">[</span><span class="token punctuation">]</span> cbuf<span class="token punctuation">)</span>
<span class="token comment">// 在write(char[] cbuf)方法的基础上增加了off参数（偏移量）和len参数（要读取的最大字符数）。</span>
<span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">[</span><span class="token punctuation">]</span> cbuf<span class="token punctuation">,</span> <span class="token keyword">int</span> off<span class="token punctuation">,</span> <span class="token keyword">int</span> len<span class="token punctuation">)</span>
<span class="token comment">// 写入字符串，等价于write(str, 0, str.length())。</span>
<span class="token function">write</span><span class="token punctuation">(</span><span class="token class-name">String</span> str<span class="token punctuation">)</span>
<span class="token comment">// 在write(String str)方法的基础上增加了off参数（偏移量）和len参数（要读取的最大字符数）。</span>
<span class="token function">write</span><span class="token punctuation">(</span><span class="token class-name">String</span> str<span class="token punctuation">,</span> <span class="token keyword">int</span> off<span class="token punctuation">,</span> <span class="token keyword">int</span> len<span class="token punctuation">)</span>
<span class="token comment">// 将指定的字符序列附加到指定的Writer对象并返回该Writer对象。</span>
<span class="token function">append</span><span class="token punctuation">(</span><span class="token class-name">CharSequence</span> csq<span class="token punctuation">)</span>
<span class="token comment">// 将指定的字符附加到指定的Writer对象并返回该Writer对象。</span>
<span class="token function">append</span><span class="token punctuation">(</span><span class="token keyword">char</span> c<span class="token punctuation">)</span>
<span class="token comment">// 刷新此输出流并强制写出所有缓冲的输出字符。</span>
<span class="token function">flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 关闭输出流释放相关的系统资源。</span>
<span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p><strong>OutputStreamWriter</strong>是字符流转换为字节流的桥梁，其子类FileWriter是基于该基础上的封装，可以直接将字符写入到文件。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 字符流转换为字节流的桥梁</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OutputStreamWriter</span> <span class="token keyword">extends</span> <span class="token class-name">Writer</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span>
<span class="token comment">// 用于写入字符到文件</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">FileWriter</span> <span class="token keyword">extends</span> <span class="token class-name">OutputStreamWriter</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>FileWriter</strong>代码示例：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">Writer</span> output <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileWriter</span><span class="token punctuation">(</span><span class="token string">"output.txt"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    output<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span><span class="token string">"你好，我是Guide。"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>输出结果：</p>
<p><img src="https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/image-20220419155802288.png" alt="img"></p>
<h4 id="字节缓冲流"><a href="#字节缓冲流" class="headerlink" title="字节缓冲流"></a>字节缓冲流</h4><p>IO操作是很消耗性能的，缓冲流将数据加载至缓冲区，一次性读取&#x2F;写入多个字节，从而避免频繁的IO操作，提高流的传输效率。字节缓冲流这里采用了装饰器模式来增强InputStream和OutputStream子类对象的功能。举个例子，我们可以通过BufferedInputStream（字节缓冲输入流）来增强FileInputStream的功能。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 新建一个BufferedInputStream对象</span>
<span class="token class-name">BufferedInputStream</span> bufferedInputStream <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>

<p>字节流和字节缓冲流的性能差别主要体现在我们使用两者的时候都是调用write(int b)和read()这两个一次只读取一个字节的方法的时候。由于字节缓冲流内部有缓冲区（字节数组），因此，字节缓冲流会先将读取到的字节存放在缓存区，大幅减少IO次数，提高读取效率。</p>
<p>我使用write(int b)和read()方法，分别通过字节流和字节缓冲流复制一个524.9 mb的PDF文件耗时对比如下：</p>
<pre class="line-numbers language-text" data-language="text"><code class="language-text">使用缓冲流复制PDF文件总耗时:15428毫秒
使用普通字节流复制PDF文件总耗时:2555062毫秒<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>

<p>两者耗时差别非常大，缓冲流耗费的时间是字节流的1&#x2F;165。测试代码如下:</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span>
<span class="token keyword">void</span> <span class="token function">copy_pdf_to_another_pdf_buffer_stream</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 记录开始时间</span>
    <span class="token keyword">long</span> start <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">BufferedInputStream</span> bis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"深入理解计算机操作系统.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
         <span class="token class-name">BufferedOutputStream</span> bos <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedOutputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"深入理解计算机操作系统-副本.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">int</span> content<span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>content <span class="token operator">=</span> bis<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            bos<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token comment">// 记录结束时间</span>
    <span class="token keyword">long</span> end <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"使用缓冲流复制PDF文件总耗时:"</span> <span class="token operator">+</span> <span class="token punctuation">(</span>end <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"毫秒"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token annotation punctuation">@Test</span>
<span class="token keyword">void</span> <span class="token function">copy_pdf_to_another_pdf_stream</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 记录开始时间</span>
    <span class="token keyword">long</span> start <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">FileInputStream</span> fis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"深入理解计算机操作系统.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
         <span class="token class-name">FileOutputStream</span> fos <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"深入理解计算机操作系统-副本.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">int</span> content<span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>content <span class="token operator">=</span> fis<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            fos<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token comment">// 记录结束时间</span>
    <span class="token keyword">long</span> end <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"使用普通流复制PDF文件总耗时:"</span> <span class="token operator">+</span> <span class="token punctuation">(</span>end <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"毫秒"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>如果是调用read(byte b[])和write(byte b[], int off, int len)这两个写入一个字节数组的方法的话，只要字节数组的大小合适，两者的性能差距其实不大，基本可以忽略。这次我们使用read(byte b[])和write(byte b[], int off, int len)方法，分别通过字节流和字节缓冲流复制一个524.9mb的PDF文件耗时对比如下：</p>
<pre class="line-numbers language-text" data-language="text"><code class="language-text">使用缓冲流复制PDF文件总耗时:695毫秒
使用普通字节流复制PDF文件总耗时:989毫秒<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>

<p>两者耗时差别不是很大，缓冲流的性能要略微好一点点。测试代码如下：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span>
<span class="token keyword">void</span> <span class="token function">copy_pdf_to_another_pdf_with_byte_array_buffer_stream</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 记录开始时间</span>
    <span class="token keyword">long</span> start <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">BufferedInputStream</span> bis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"深入理解计算机操作系统.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
         <span class="token class-name">BufferedOutputStream</span> bos <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedOutputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"深入理解计算机操作系统-副本.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">int</span> len<span class="token punctuation">;</span>
        <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> bytes <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token number">4</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>len <span class="token operator">=</span> bis<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>bytes<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            bos<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>bytes<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> len<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token comment">// 记录结束时间</span>
    <span class="token keyword">long</span> end <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"使用缓冲流复制PDF文件总耗时:"</span> <span class="token operator">+</span> <span class="token punctuation">(</span>end <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"毫秒"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token annotation punctuation">@Test</span>
<span class="token keyword">void</span> <span class="token function">copy_pdf_to_another_pdf_with_byte_array_stream</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 记录开始时间</span>
    <span class="token keyword">long</span> start <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">FileInputStream</span> fis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"深入理解计算机操作系统.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
         <span class="token class-name">FileOutputStream</span> fos <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"深入理解计算机操作系统-副本.pdf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">int</span> len<span class="token punctuation">;</span>
        <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> bytes <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token number">4</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>len <span class="token operator">=</span> fis<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>bytes<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            fos<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>bytes<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> len<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token comment">// 记录结束时间</span>
    <span class="token keyword">long</span> end <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"使用普通流复制PDF文件总耗时:"</span> <span class="token operator">+</span> <span class="token punctuation">(</span>end <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"毫秒"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>BufferedInputStream（字节缓冲输入流）</strong></p>
<p>BufferedInputStream从源头（通常是文件）读取数据（字节信息）到内存的过程中不会一个字节一个字节的读取，而是会先将读取到的字节存放在缓存区，并从内部缓冲区中单独读取字节。这样大幅减少了IO次数，提高了读取效率。BufferedInputStream内部维护了一个缓冲区，这个缓冲区实际就是一个字节数组，通过阅读BufferedInputStream源码即可得到这个结论。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">BufferedInputStream</span> <span class="token keyword">extends</span> <span class="token class-name">FilterInputStream</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 内部缓冲区数组</span>
    <span class="token keyword">protected</span> <span class="token keyword">volatile</span> <span class="token keyword">byte</span> buf<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token comment">// 缓冲区的默认大小</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">int</span> <span class="token constant">DEFAULT_BUFFER_SIZE</span> <span class="token operator">=</span> <span class="token number">8192</span><span class="token punctuation">;</span>
    <span class="token comment">// 使用默认的缓冲区大小</span>
    <span class="token keyword">public</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token class-name">InputStream</span> in<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">this</span><span class="token punctuation">(</span>in<span class="token punctuation">,</span> <span class="token constant">DEFAULT_BUFFER_SIZE</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token comment">// 自定义缓冲区大小</span>
    <span class="token keyword">public</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token class-name">InputStream</span> in<span class="token punctuation">,</span> <span class="token keyword">int</span> size<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">super</span><span class="token punctuation">(</span>in<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>size <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"Buffer size &lt;= 0"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        buf <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span>size<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>缓冲区的大小默认为<strong>8192</strong>字节，当然了，你也可以通过BufferedInputStream(InputStream in, int size)这个构造方法来指定缓冲区的大小。</p>
<p><strong>BufferedOutputStream（字节缓冲输出流）</strong></p>
<p>BufferedOutputStream将数据（字节信息）写入到目的地（通常是文件）的过程中不会一个字节一个字节的写入，而是会先将要写入的字节存放在缓存区，并从内部缓冲区中单独写入字节。这样大幅减少了IO次数，提高了读取效率</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">BufferedOutputStream</span> bos <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedOutputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token string">"output.txt"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> array <span class="token operator">=</span> <span class="token string">"JavaGuide"</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    bos<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>array<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>类似于BufferedInputStream，BufferedOutputStream内部也维护了一个缓冲区，并且，这个缓存区的大小也是<strong>8192</strong>字节。</p>
<h4 id="字符缓冲流"><a href="#字符缓冲流" class="headerlink" title="字符缓冲流"></a>字符缓冲流</h4><p>BufferedReader（字符缓冲输入流）和BufferedWriter（字符缓冲输出流）类似于BufferedInputStream（字节缓冲输入流）和BufferedOutputStream（字节缓冲输入流），内部都维护了一个字节数组作为缓冲区。不过，前者主要是用来操作字符信息。</p>
<h4 id="打印流"><a href="#打印流" class="headerlink" title="打印流"></a>打印流</h4><p>下面这段代码大家经常使用吧？</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"Hello！"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Hello！"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>

<p>System.out实际是用于获取一个PrintStream对象，print方法实际调用的是PrintStream对象的write方法。PrintStream属于字节打印流，与之对应的是PrintWriter（字符打印流）。PrintStream是OutputStream的子类，PrintWriter是Writer的子类。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PrintStream</span> <span class="token keyword">extends</span> <span class="token class-name">FilterOutputStream</span>
    <span class="token keyword">implements</span> <span class="token class-name">Appendable</span><span class="token punctuation">,</span> <span class="token class-name">Closeable</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PrintWriter</span> <span class="token keyword">extends</span> <span class="token class-name">Writer</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="随机访问流"><a href="#随机访问流" class="headerlink" title="随机访问流"></a>随机访问流</h4><p>这里要介绍的随机访问流指的是支持随意跳转到文件的任意位置进行读写的RandomAccessFile。</p>
<p>RandomAccessFile的构造方法如下，我们可以指定mode（读写模式）。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// openAndDelete参数默认为false表示打开文件并且这个文件不会被删除</span>
<span class="token keyword">public</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token class-name">File</span> file<span class="token punctuation">,</span> <span class="token class-name">String</span> mode<span class="token punctuation">)</span>
    <span class="token keyword">throws</span> <span class="token class-name">FileNotFoundException</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">this</span><span class="token punctuation">(</span>file<span class="token punctuation">,</span> mode<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>
<span class="token comment">// 私有方法</span>
<span class="token keyword">private</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token class-name">File</span> file<span class="token punctuation">,</span> <span class="token class-name">String</span> mode<span class="token punctuation">,</span> <span class="token keyword">boolean</span> openAndDelete<span class="token punctuation">)</span>  <span class="token keyword">throws</span> <span class="token class-name">FileNotFoundException</span><span class="token punctuation">&#123;</span>
  <span class="token comment">// 省略大部分代码</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>读写模式主要有下面四种：</p>
<ul>
<li>r:只读模式。</li>
<li>rw:读写模式</li>
<li>rws:相对于rw，rws同步更新对“文件的内容”或“元数据”的修改到外部存储设备。</li>
<li>rwd:相对于rw，rwd同步更新对“文件的内容”的修改到外部存储设备。</li>
</ul>
<p>文件内容指的是文件中实际保存的数据，元数据则是用来描述文件属性比如文件的大小信息、创建和修改时间。RandomAccessFile中有一个文件指针用来表示下一个将要被写入或者读取的字节所处的位置。我们可以通过RandomAccessFile的seek(long pos)方法来设置文件指针的偏移量（距文件开头pos个字节处）。如果想要获取文件指针当前的位置的话，可以使用getFilePointer()方法。</p>
<p>RandomAccessFile代码示例：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">RandomAccessFile</span> randomAccessFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">File</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"rw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"读取之前的偏移量："</span> <span class="token operator">+</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">getFilePointer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">",当前读取到的字符"</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"，读取之后的偏移量："</span> <span class="token operator">+</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">getFilePointer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 指针当前偏移量为6</span>
randomAccessFile<span class="token punctuation">.</span><span class="token function">seek</span><span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"读取之前的偏移量："</span> <span class="token operator">+</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">getFilePointer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">",当前读取到的字符"</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"，读取之后的偏移量："</span> <span class="token operator">+</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">getFilePointer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 从偏移量7的位置开始往后写入字节数据</span>
randomAccessFile<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">&#123;</span><span class="token char">'H'</span><span class="token punctuation">,</span> <span class="token char">'I'</span><span class="token punctuation">,</span> <span class="token char">'J'</span><span class="token punctuation">,</span> <span class="token char">'K'</span><span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 指针当前偏移量为0，回到起始位置</span>
randomAccessFile<span class="token punctuation">.</span><span class="token function">seek</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"读取之前的偏移量："</span> <span class="token operator">+</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">getFilePointer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">",当前读取到的字符"</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"，读取之后的偏移量："</span> <span class="token operator">+</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">getFilePointer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>input.txt文件内容：</p>
<p><img src="https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/image-20220421162050158.png" alt="img"></p>
<p>输出：</p>
<pre class="line-numbers language-text" data-language="text"><code class="language-text">读取之前的偏移量：0,当前读取到的字符A，读取之后的偏移量：1
读取之前的偏移量：6,当前读取到的字符G，读取之后的偏移量：7
读取之前的偏移量：0,当前读取到的字符A，读取之后的偏移量：1<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>

<p>input.txt文件内容变为ABCDEFGHIJK。</p>
<p>RandomAccessFile的write方法在写入对象的时候如果对应的位置已经有数据的话，会将其覆盖掉。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">RandomAccessFile</span> randomAccessFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">File</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"rw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
randomAccessFile<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">&#123;</span><span class="token char">'H'</span><span class="token punctuation">,</span> <span class="token char">'I'</span><span class="token punctuation">,</span> <span class="token char">'J'</span><span class="token punctuation">,</span> <span class="token char">'K'</span><span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>

<p>假设运行上面这段程序之前input.txt文件内容变为ABCD，运行之后则变为HIJK。</p>
<p>RandomAccessFile比较常见的一个应用就是实现大文件的<strong>断点续传</strong>。何谓断点续传？简单来说就是上传文件中途暂停或失败（比如遇到网络问题）之后，不需要重新上传，只需要上传那些未成功上传的文件分片即可。分片（先将文件切分成多个文件分片）上传是断点续传的基础。RandomAccessFile可以帮助我们合并文件分片，示例代码如下：</p>
<p><img src="https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/java/io/20210609164749122.png" alt="img"></p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://javaguide.cn/java/io/io-basis.html">原文链接</a><br><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/CYXVyNsNm36SCu_jrAp5qQ">高效快捷读写文件之RandomAccessFile类解说</a></p>
</blockquote>
<h3 id="IO设计模式总结"><a href="#IO设计模式总结" class="headerlink" title="IO设计模式总结"></a>IO设计模式总结</h3><h4 id="装饰器模式"><a href="#装饰器模式" class="headerlink" title="装饰器模式"></a>装饰器模式</h4><p><strong>装饰器（Decorator）模式</strong>可以在不改变原有对象的情况下拓展其功能。</p>
<p>装饰器模式通过组合替代继承来扩展原始类的功能，在一些继承关系比较复杂的场景（IO这一场景各种类的继承关系就比较复杂）更加实用。对于字节流来说，FilterInputStream（对应输入流）和FilterOutputStream（对应输出流）是装饰器模式的核心，分别用于增强InputStream和OutputStream子类对象的功能。我们常见的BufferedInputStream(字节缓冲输入流)、DataInputStream等等都是FilterInputStream的子类，BufferedOutputStream（字节缓冲输出流）、DataOutputStream等等都是FilterOutputStream的子类。举个例子，我们可以通过BufferedInputStream（字节缓冲输入流）来增强FileInputStream的功能。</p>
<p>BufferedInputStream构造函数如下：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token class-name">InputStream</span> in<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">this</span><span class="token punctuation">(</span>in<span class="token punctuation">,</span> <span class="token constant">DEFAULT_BUFFER_SIZE</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token keyword">public</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token class-name">InputStream</span> in<span class="token punctuation">,</span> <span class="token keyword">int</span> size<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">super</span><span class="token punctuation">(</span>in<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>size <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"Buffer size &lt;= 0"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    buf <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span>size<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>可以看出，BufferedInputStream的构造函数其中的一个参数就是InputStream。</p>
<p>BufferedInputStream代码示例：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">BufferedInputStream</span> bis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">int</span> content<span class="token punctuation">;</span>
    <span class="token keyword">long</span> skip <span class="token operator">=</span> bis<span class="token punctuation">.</span><span class="token function">skip</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>content <span class="token operator">=</span> bis<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>这个时候，你可以会想了：<strong>为啥我们直接不弄一个BufferedFileInputStream（字符缓冲文件输入流）呢</strong>？</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">BufferedFileInputStream</span> bfis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedFileInputStream</span><span class="token punctuation">(</span><span class="token string">"input.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>

<p>如果InputStream的子类比较少的话，这样做是没问题的。不过，InputStream的子类实在太多，继承关系也太复杂了。如果我们为每一个子类都定制一个对应的缓冲输入流，那岂不是太麻烦了。如果你对IO流比较熟悉的话，你会发现ZipInputStream和ZipOutputStream还可以分别增强BufferedInputStream和BufferedOutputStream的能力。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">BufferedInputStream</span> bis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedInputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">ZipInputStream</span> zis <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ZipInputStream</span><span class="token punctuation">(</span>bis<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token class-name">BufferedOutputStream</span> bos <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedOutputStream</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">ZipOutputStream</span> zipOut <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ZipOutputStream</span><span class="token punctuation">(</span>bos<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>ZipInputStream和ZipOutputStream分别继承自InflaterInputStream和DeflaterOutputStream。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span>
<span class="token keyword">class</span> <span class="token class-name">InflaterInputStream</span> <span class="token keyword">extends</span> <span class="token class-name">FilterInputStream</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span>

<span class="token keyword">public</span>
<span class="token keyword">class</span> <span class="token class-name">DeflaterOutputStream</span> <span class="token keyword">extends</span> <span class="token class-name">FilterOutputStream</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>这也是装饰器模式很重要的一个特征，那就是可以对原始类嵌套使用多个装饰器。为了实现这一效果，装饰器类需要跟原始类继承相同的抽象类或者实现相同的接口。上面介绍到的这些IO相关的装饰类和原始类共同的父类是InputStream和OutputStream。对于字符流来说，BufferedReader可以用来增加Reader（字符输入流）子类的功能，BufferedWriter可以用来增加Writer（字符输出流）子类的功能。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">BufferedWriter</span> bw <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedWriter</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">OutputStreamWriter</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token string">"UTF-8"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>

<p>IO流中的装饰器模式应用的例子实在是太多了，不需要特意记忆，完全没必要哈！搞清了装饰器模式的核心之后，你在使用的时候自然就会知道哪些地方运用到了装饰器模式。</p>
<h4 id="适配器模式"><a href="#适配器模式" class="headerlink" title="适配器模式"></a>适配器模式</h4><p><strong>适配器（Adapter Pattern）模式</strong>主要用于接口互不兼容的类的协调工作，你可以将其联想到我们日常经常使用的电源适配器。</p>
<p>适配器模式中存在被适配的对象或者类称为<strong>适配者（Adaptee）</strong>，作用于适配者的对象或者类称为适配器(Adapter)。适配器分为对象适配器和类适配器。类适配器使用继承关系来实现，对象适配器使用组合关系来实现。</p>
<p>IO流中的字符流和字节流的接口不同，它们之间可以协调工作就是基于适配器模式来做的，更准确点来说是对象适配器。通过适配器，我们可以将字节流对象适配成一个字符流对象，这样我们可以直接通过字节流对象来读取或者写入字符数据。</p>
<p>InputStreamReader和OutputStreamWriter就是两个适配器(Adapter)，同时，它们两个也是字节流和字符流之间的桥梁。InputStreamReader使用StreamDecoder（流解码器）对字节进行解码，实现字节流到字符流的转换，OutputStreamWriter使用StreamEncoder（流编码器）对字符进行编码，实现字符流到字节流的转换。</p>
<p>InputStream和OutputStream的子类是被适配者，InputStreamReader和OutputStreamWriter是适配器。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// InputStreamReader是适配器，FileInputStream是被适配的类</span>
<span class="token class-name">InputStreamReader</span> isr <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">InputStreamReader</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"UTF-8"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// BufferedReader增强InputStreamReader的功能（装饰器模式）</span>
<span class="token class-name">BufferedReader</span> bufferedReader <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BufferedReader</span><span class="token punctuation">(</span>isr<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>

<p>java.io.InputStreamReader部分源码：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">InputStreamReader</span> <span class="token keyword">extends</span> <span class="token class-name">Reader</span> <span class="token punctuation">&#123;</span>
	<span class="token comment">//用于解码的对象</span>
	<span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">StreamDecoder</span> sd<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token class-name">InputStreamReader</span><span class="token punctuation">(</span><span class="token class-name">InputStream</span> in<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">super</span><span class="token punctuation">(</span>in<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
            <span class="token comment">// 获取StreamDecoder对象</span>
            sd <span class="token operator">=</span> <span class="token class-name">StreamDecoder</span><span class="token punctuation">.</span><span class="token function">forInputStreamReader</span><span class="token punctuation">(</span>in<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">)</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">UnsupportedEncodingException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token comment">// 使用StreamDecoder对象做具体的读取工作</span>
	<span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> sd<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>java.io.OutputStreamWriter部分源码：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OutputStreamWriter</span> <span class="token keyword">extends</span> <span class="token class-name">Writer</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 用于编码的对象</span>
    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">StreamEncoder</span> se<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token class-name">OutputStreamWriter</span><span class="token punctuation">(</span><span class="token class-name">OutputStream</span> out<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">super</span><span class="token punctuation">(</span>out<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
           <span class="token comment">// 获取StreamEncoder对象</span>
            se <span class="token operator">=</span> <span class="token class-name">StreamEncoder</span><span class="token punctuation">.</span><span class="token function">forOutputStreamWriter</span><span class="token punctuation">(</span>out<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">)</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">UnsupportedEncodingException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token comment">// 使用StreamEncoder对象做具体的写入工作</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token keyword">int</span> c<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">&#123;</span>
        se<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><strong>适配器模式和装饰器模式有什么区别呢</strong>？</p>
<p><strong>装饰器模式</strong>更侧重于动态地增强原始类的功能，装饰器类需要跟原始类继承相同的抽象类或者实现相同的接口。并且，装饰器模式支持对原始类嵌套使用多个装饰器。</p>
<p><strong>适配器模式</strong>更侧重于让接口不兼容而不能交互的类可以一起工作，当我们调用适配器对应的方法时，适配器内部会调用适配者类或者和适配类相关的类的方法，这个过程透明的。就比如说StreamDecoder（流解码器）和StreamEncoder（流编码器）就是分别基于InputStream和OutputStream来获取FileChannel对象并调用对应的read方法和write方法进行字节数据的读取和写入。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">StreamDecoder</span><span class="token punctuation">(</span><span class="token class-name">InputStream</span> in<span class="token punctuation">,</span> <span class="token class-name">Object</span> lock<span class="token punctuation">,</span> <span class="token class-name">CharsetDecoder</span> dec<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 省略大部分代码</span>
    <span class="token comment">// 根据InputStream对象获取FileChannel对象</span>
    ch <span class="token operator">=</span> <span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">FileInputStream</span><span class="token punctuation">)</span>in<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>适配器和适配者两者不需要继承相同的抽象类或者实现相同的接口。另外，FutureTask类使用了适配器模式，Executors的内部类RunnableAdapter实现属于适配器，用于将Runnable适配成Callable。FutureTask参数包含Runnable的一个构造方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">FutureTask</span><span class="token punctuation">(</span><span class="token class-name">Runnable</span> runnable<span class="token punctuation">,</span> <span class="token class-name">V</span> result<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 调用Executors类的callable方法</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>callable <span class="token operator">=</span> <span class="token class-name">Executors</span><span class="token punctuation">.</span><span class="token function">callable</span><span class="token punctuation">(</span>runnable<span class="token punctuation">,</span> result<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>state <span class="token operator">=</span> <span class="token constant">NEW</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>Executors中对应的方法和适配器：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 实际调用的是Executors的内部类RunnableAdapter的构造方法</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token class-name">Callable</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token function">callable</span><span class="token punctuation">(</span><span class="token class-name">Runnable</span> task<span class="token punctuation">,</span> <span class="token class-name">T</span> result<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>task <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">NullPointerException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">RunnableAdapter</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>task<span class="token punctuation">,</span> result<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>
<span class="token comment">// 适配器</span>
<span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">RunnableAdapter</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token keyword">implements</span> <span class="token class-name">Callable</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">T</span><span class="token punctuation">></span></span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">final</span> <span class="token class-name">Runnable</span> task<span class="token punctuation">;</span>
    <span class="token keyword">final</span> <span class="token class-name">T</span> result<span class="token punctuation">;</span>
    <span class="token class-name">RunnableAdapter</span><span class="token punctuation">(</span><span class="token class-name">Runnable</span> task<span class="token punctuation">,</span> <span class="token class-name">T</span> result<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>task <span class="token operator">=</span> task<span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>result <span class="token operator">=</span> result<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token keyword">public</span> <span class="token class-name">T</span> <span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        task<span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> result<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="工厂模式"><a href="#工厂模式" class="headerlink" title="工厂模式"></a>工厂模式</h4><p>工厂模式用于创建对象，NIO中大量用到了工厂模式，比如Files类的newInputStream方法用于创建InputStream对象（静态工厂）、Paths类的get方法创建Path对象（静态工厂）、ZipFileSystem类（sun.nio包下的类，属于java.nio相关的一些内部实现）的getPath的方法创建Path对象（简单工厂）。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">InputStream</span> is <span class="token class-name">Files</span><span class="token punctuation">.</span><span class="token function">newInputStream</span><span class="token punctuation">(</span><span class="token class-name">Paths</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>generatorLogoPath<span class="token punctuation">)</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>

<h4 id="观察者模式"><a href="#观察者模式" class="headerlink" title="观察者模式"></a>观察者模式</h4><p>NIO中的文件目录监听服务使用到了观察者模式。NIO中的文件目录监听服务基于WatchService接口和Watchable接口。WatchService属于观察者，Watchable属于被观察者。Watchable接口定义了一个用于将对象注册到WatchService（监控服务）并绑定监听事件的方法register。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Path</span>
    <span class="token keyword">extends</span> <span class="token class-name">Comparable</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Path</span><span class="token punctuation">></span></span><span class="token punctuation">,</span> <span class="token class-name">Iterable</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Path</span><span class="token punctuation">></span></span><span class="token punctuation">,</span> <span class="token class-name">Watchable</span><span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span>

<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Watchable</span> <span class="token punctuation">&#123;</span>
    <span class="token class-name">WatchKey</span> <span class="token function">register</span><span class="token punctuation">(</span><span class="token class-name">WatchService</span> watcher<span class="token punctuation">,</span>
                      <span class="token class-name">WatchEvent<span class="token punctuation">.</span>Kind</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span><span class="token punctuation">[</span><span class="token punctuation">]</span> events<span class="token punctuation">,</span>
                      <span class="token class-name">WatchEvent<span class="token punctuation">.</span>Modifier</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> modifiers<span class="token punctuation">)</span>
        <span class="token keyword">throws</span> <span class="token class-name">IOException</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>WatchService用于监听文件目录的变化，同一个WatchService对象能够监听多个文件目录。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 创建WatchService对象</span>
<span class="token class-name">WatchService</span> watchService <span class="token operator">=</span> <span class="token class-name">FileSystems</span><span class="token punctuation">.</span><span class="token function">getDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">newWatchService</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 初始化一个被监控文件夹的Path类:</span>
<span class="token class-name">Path</span> path <span class="token operator">=</span> <span class="token class-name">Paths</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"workingDirectory"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 将这个path对象注册到WatchService（监控服务）中去</span>
<span class="token class-name">WatchKey</span> watchKey <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>
watchService<span class="token punctuation">,</span> <span class="token class-name">StandardWatchEventKinds</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>Path类register方法的第二个参数events（需要监听的事件）为可变长参数，也就是说我们可以同时监听多种事件。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">WatchKey</span> <span class="token function">register</span><span class="token punctuation">(</span><span class="token class-name">WatchService</span> watcher<span class="token punctuation">,</span>
                  <span class="token class-name">WatchEvent<span class="token punctuation">.</span>Kind</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> events<span class="token punctuation">)</span>
    <span class="token keyword">throws</span> <span class="token class-name">IOException</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>

<p>常用的监听事件有3种：</p>
<ul>
<li><strong>StandardWatchEventKinds.ENTRY_CREATE</strong>:文件创建。</li>
<li><strong>StandardWatchEventKinds.ENTRY_DELETE</strong>:文件删除。</li>
<li><strong>StandardWatchEventKinds.ENTRY_MODIFY</strong>:文件修改。</li>
</ul>
<p>register方法返回WatchKey对象，通过WatchKey对象可以获取事件的具体信息比如文件目录下是创建、删除还是修改了文件、创建、删除或者修改的文件的具体名称是什么。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">WatchKey</span> key<span class="token punctuation">;</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>key <span class="token operator">=</span> watchService<span class="token punctuation">.</span><span class="token function">take</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">WatchEvent</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> event <span class="token operator">:</span> key<span class="token punctuation">.</span><span class="token function">pollEvents</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token comment">// 可以调用WatchEvent对象的方法做一些事情比如输出事件的具体上下文信息</span>
    <span class="token punctuation">&#125;</span>
    key<span class="token punctuation">.</span><span class="token function">reset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>WatchService内部是通过一个daemon thread（守护线程）采用定期轮询的方式来检测文件的变化，简化后的源码如下所示。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">PollingWatchService</span>
    <span class="token keyword">extends</span> <span class="token class-name">AbstractWatchService</span>
<span class="token punctuation">&#123;</span>
    <span class="token comment">// 定义一个daemon thread（守护线程）轮询检测文件变化</span>
    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">ScheduledExecutorService</span> scheduledExecutor<span class="token punctuation">;</span>

    <span class="token class-name">PollingWatchService</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        scheduledExecutor <span class="token operator">=</span> <span class="token class-name">Executors</span>
            <span class="token punctuation">.</span><span class="token function">newSingleThreadScheduledExecutor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ThreadFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                 <span class="token annotation punctuation">@Override</span>
                 <span class="token keyword">public</span> <span class="token class-name">Thread</span> <span class="token function">newThread</span><span class="token punctuation">(</span><span class="token class-name">Runnable</span> r<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                     <span class="token class-name">Thread</span> t <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span>r<span class="token punctuation">)</span><span class="token punctuation">;</span>
                     t<span class="token punctuation">.</span><span class="token function">setDaemon</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                     <span class="token keyword">return</span> t<span class="token punctuation">;</span>
                 <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

  <span class="token keyword">void</span> <span class="token function">enable</span><span class="token punctuation">(</span><span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token class-name">WatchEvent<span class="token punctuation">.</span>Kind</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> events<span class="token punctuation">,</span> <span class="token keyword">long</span> period<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token comment">// 更新监听事件</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>events <span class="token operator">=</span> events<span class="token punctuation">;</span>

        <span class="token comment">// 开启定期轮询</span>
      <span class="token class-name">Runnable</span> thunk <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Runnable</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token function">poll</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>poller <span class="token operator">=</span> scheduledExecutor
        <span class="token punctuation">.</span><span class="token function">scheduleAtFixedRate</span><span class="token punctuation">(</span>thunk<span class="token punctuation">,</span> period<span class="token punctuation">,</span> period<span class="token punctuation">,</span> <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span><span class="token constant">SECONDS</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://javaguide.cn/java/io/io-design-patterns.html">原文链接</a></p>
</blockquote>
<h3 id="IO模型详解"><a href="#IO模型详解" class="headerlink" title="IO模型详解"></a>IO模型详解</h3><h4 id="BIO-Blocking-I-x2F-O"><a href="#BIO-Blocking-I-x2F-O" class="headerlink" title="BIO(Blocking I&#x2F;O)"></a>BIO(Blocking I&#x2F;O)</h4><p><strong>BIO属于同步阻塞IO模型</strong>。</p>
<p>同步阻塞IO模型中，应用程序发起read调用后，会一直阻塞，直到内核把数据拷贝到用户空间。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6a9e704af49b4380bb686f0c96d33b81~tplv-k3u1fbpfcp-watermark.image" alt="图源：《深入拆解Tomcat&amp;Jetty》"></p>
<p>在客户端连接数量不高的情况下，是没问题的。但是，当面对十万甚至百万级连接的时候，传统的BIO模型是无能为力的。因此，我们需要一种更高效的I&#x2F;O处理模型来应对更高的并发量。</p>
<h4 id="NIO-Non-blocking-x2F-New-I-x2F-O"><a href="#NIO-Non-blocking-x2F-New-I-x2F-O" class="headerlink" title="NIO(Non-blocking&#x2F;New I&#x2F;O)"></a>NIO(Non-blocking&#x2F;New I&#x2F;O)</h4><p>Java中的NIO于Java1.4中引入，对应<code>java.nio</code>包，提供了<code>Channel</code>,<code>Selector</code>，<code>Buffer</code>等抽象。NIO中的N可以理解为Non-blocking，不单纯是New。它是支持面向缓冲的，基于通道的I&#x2F;O操作方法。对于高负载、高并发的（网络）应用，应使用NIO。Java中的NIO可以看作是<strong>I&#x2F;O多路复用模型</strong>。也有很多人认为，Java中的NIO属于同步非阻塞IO模型。</p>
<p>我们先来看看<strong>同步非阻塞IO模型</strong>。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bb174e22dbe04bb79fe3fc126aed0c61~tplv-k3u1fbpfcp-watermark.image" alt="图源：《深入拆解Tomcat&amp;Jetty》"></p>
<p>同步非阻塞IO模型中，应用程序会一直发起read调用，等待数据从内核空间拷贝到用户空间的这段时间里，线程依然是阻塞的，直到在内核把数据拷贝到用户空间。相比于同步阻塞IO模型，同步非阻塞IO模型确实有了很大改进。通过轮询操作，避免了一直阻塞。但是，这种IO模型同样存在问题：应用程序不断进行I&#x2F;O系统调用轮询数据是否已经准备好的过程是十分消耗CPU资源的。这个时候，<strong>I&#x2F;O多路复用模型</strong>就上场了。</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/88ff862764024c3b8567367df11df6ab~tplv-k3u1fbpfcp-watermark.image" alt="img"></p>
<p>IO多路复用模型中，线程首先发起select调用，询问内核数据是否准备就绪，等内核把数据准备好了，用户线程再发起read调用。read调用的过程（数据从内核空间-&gt;用户空间）还是阻塞的。目前支持IO多路复用的系统调用，有select，epoll等等。select系统调用，目前几乎在所有的操作系统上都有支持。</p>
<ul>
<li><strong>select调用</strong>：内核提供的系统调用，它支持一次查询多个系统调用的可用状态。几乎所有的操作系统都支持。</li>
<li><strong>epoll调用</strong>：linux2.6内核，属于select调用的增强版本，优化了IO的执行效率。</li>
</ul>
<p><strong>IO多路复用模型，通过减少无效的系统调用，减少了对CPU资源的消耗。</strong></p>
<p>Java中的NIO，有一个非常重要的<strong>选择器(Selector)<strong>的概念，也可以被称为</strong>多路复用器</strong>。通过它，只需要一个线程便可以管理多个客户端连接。当客户端数据到了之后，才会为其服务。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0f483f2437ce4ecdb180134270a00144~tplv-k3u1fbpfcp-watermark.image" alt="img"></p>
<h4 id="AIO-Asynchronous-I-x2F-O"><a href="#AIO-Asynchronous-I-x2F-O" class="headerlink" title="AIO(Asynchronous I&#x2F;O)"></a>AIO(Asynchronous I&#x2F;O)</h4><p>AIO也就是NIO2。Java7中引入了NIO的改进版NIO2,它是异步IO模型。</p>
<p>异步IO是基于事件和回调机制实现的，也就是应用操作之后会直接返回，不会堵塞在那里，当后台处理完成，操作系统会通知相应的线程进行后续的操作。</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3077e72a1af049559e81d18205b56fd7~tplv-k3u1fbpfcp-watermark.image" alt="img"></p>
<p>目前来说AIO的应用还不是很广泛。Netty之前也尝试使用过AIO，不过又放弃了。这是因为，Netty使用了AIO之后，在Linux系统上的性能并没有多少提升。最后，来一张图，简单总结一下Java中的BIO、NIO、AIO。</p>
<p><img src="https://images.xiaozhuanlan.com/photo/2020/33b193457c928ae02217480f994814b6.png" alt="img"></p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://javaguide.cn/java/io/io-model.html">原文链接</a></p>
</blockquote>
<h3 id="相关文章"><a href="#相关文章" class="headerlink" title="相关文章"></a>相关文章</h3><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MzkzODE3OTI0Ng==&mid=2247491115&idx=1&sn=14f1712cc787befd6c78d64612a00f95&source=41#wechat_redirect">如果有人再问你Java IO，把这篇文章砸他头上</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/asMxiq9yIPa6aaZenfDBmQ">Java I&#x2F;O体系从原理到应用，这一篇全说清楚了</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/p5qM2UJ1uIWyongfVpRbCg">理解Java的IO(同步非同步阻塞非阻塞)</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VsCINjx0sPhhBC62k223jw">用三张图看透I&#x2F;O，学习区分同异步阻塞</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5iTAaEZUWkqa5ESZ9lPL6Q">1.6万字长文带你读懂Java IO</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/77G2NxfjZlT-icfqrHCizQ">看一遍就能理解的IO模型详解</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_pW0v7a7tKbGZpi3lRQVcw">读取文件时，程序经历了什么？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5pRJ6qP-lRFDTd38BkdMzw">常见的IO模型有哪些？Java中BIO、NIO、AIO的区别？</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Lda80PTxJnd5oZh5VWSi4Q">NIO和IO到底有什么区别？别说你不会！</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/H5sNv_a992MKUKXxK2QQEA">万字长文：助你攻破JAVA NIO技术壁垒</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/6PsSQXamf6w0w-B-ZgGNsg">JAVA语言异步非阻塞设计模式</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/pS2SXhmF7O3Gk8WRkCFZaw">一篇带你彻底读懂IO流技术！</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LJS6joxDCt71M9tEJ6GxFA">文件读写操作与常用技巧分享</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/emjHDexsHoIFmZGhwwrKfA">IO流为什么必须手动关闭，不能像其他的对象坐等GC回收?</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/GAQxxEmYbE8Sy3oS9ePvhA">面试官：BIO、NIO、AIO之间有什么区别？</a></td>
</tr>
<tr>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ibBQ56iksZCM6CJmUD6zmw">看一遍就理解：IO模型详解</a></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<h2 id="NIO"><a href="#NIO" class="headerlink" title="NIO"></a>NIO</h2><blockquote>
<p><a target="_blank" rel="noopener" href="https://github.com/xmxe/demo/tree/master/study-demo/src/main/java/com/xmxe/jdkfeature/nio">NIO demo</a><br><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/XEOYvRhtcOK2J7kdRIZcXQ">浅析Java NIO</a></p>
</blockquote>
<p>NIO主要有三大核心部分：Channel(通道)，Buffer(缓冲区),Selector(选择区)。<br>IO是面向流的，NIO是面向缓冲区的。传统IO基于字节流和字符流进行操作，而NIO基于Channel和Buffer进行操作，数据总是从通道读取到缓冲区中，或者从缓冲区写入到通道中。</p>
<h3 id="Buffer"><a href="#Buffer" class="headerlink" title="Buffer"></a>Buffer</h3><p>NIO中的关键Buffer实现有：ByteBuffer,CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer,ShortBuffer。分别对应基本数据类型:byte,char,double,float,int,long,short。NIO中还有MappedByteBuffer,HeapByteBuffer,DirectByteBuffer等缓冲区,本质上是一块可以写入数据，然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象，并提供了一组方法，用来方便的访问该块内存。这块内存被包装成NIO。</p>
<p>Buffer对象，并提供了一组方法，用来方便的访问该块内存，Buffer：顾名思义是一块缓冲区，实际上是一个容器，一个连续数组。Channel提供从文件、网络读取数据的渠道，但是读写的数据都必须经过Buffer。<br>向Buffer中写数据：从Channel到Buffer使用fileChannel.read(buf).或者通过Buffer的put()方法 buf.put()<br>从Buffer中读取数据：从Buffer到Channel使用channel.write(buf)) .或者使用get()方法 buf.get()</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**
* JAVA处理大文件，一般用BufferedReader,BufferedInputStream这类带缓冲的IO类，不过如果文件超大的话，更快的方式是采用MappedByteBuffer。MappedByteBuffer是NIO引入的文件内存映射方案，读写性能极高。NIO最主要的就是实现了对异步操作的支持。其中一种通过把一个套接字通道(SocketChannel)注册到一个选择器(Selector)中,不时调用后者的选择(select)方法就能返回满足的选择键(SelectionKey),键中包含了SOCKET事件信息。这就是select模型。
*
* FileChannel提供了map方法来把文件影射为内存映像文件：MappedByteBuffer map(int mode,long position,long size); 可以把文件的从position开始的size大小的区域映射为内存映像文件，mode指出了可访问该内存映像文件的方式:READ_ONLY（只读）:试图修改得到的缓冲区将导致抛出 ReadOnlyBufferException.(MapMode.READ_ONLY);READ_WRITE（读/写）:对得到的缓冲区的更改最终将传播到文件；该更改对映射到同一文件的其他程序不一定是可见的(MapMode.READ_WRITE);PRIVATE（专用）:对得到的缓冲区的更改不会传播到文件，并且该更改对映射到同一文件的其他程序也不是可见的；相反，会创建缓冲区已修改部分的专用副本。 (MapMode.PRIVATE)

* MappedByteBuffer是ByteBuffer的子类，其扩充了三个方法：force()：缓冲区是READ_WRITE模式下，此方法对缓冲区内容的修改强行写入文件；load()：将缓冲区的内容载入内存，并返回该缓冲区的引用；isLoaded()：如果缓冲区的内容在物理内存中，则返回真，否则返回假
*/</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">mappedByteBuffer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
    <span class="token class-name">File</span> file <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">File</span><span class="token punctuation">(</span><span class="token string">"D://data.txt"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">long</span> len <span class="token operator">=</span> file<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> ds <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span> len<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">RandomAccessFile</span> randomAccessFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span>file<span class="token punctuation">,</span> <span class="token string">"r"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token class-name">MappedByteBuffer</span> mappedByteBuffer <span class="token operator">=</span> randomAccessFile<span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token class-name">FileChannel<span class="token punctuation">.</span>MapMode</span><span class="token punctuation">.</span><span class="token constant">READ_ONLY</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> len<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> offset <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> offset <span class="token operator">&lt;</span> len<span class="token punctuation">;</span> offset<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">byte</span> b <span class="token operator">=</span> mappedByteBuffer<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            ds<span class="token punctuation">[</span>offset<span class="token punctuation">]</span> <span class="token operator">=</span> b<span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token class-name">Scanner</span> scan <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Scanner</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ByteArrayInputStream</span><span class="token punctuation">(</span>ds<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">useDelimiter</span><span class="token punctuation">(</span><span class="token string">" "</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span>scan<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        	<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span>scan<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">" "</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
       e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
        
<span class="token comment">/**
* map过程
* FileChannel提供了map方法把文件映射到虚拟内存，通常情况可以映射整个文件，如果文件比较大，可以进行分段映射。FileChannel中的几个变量：MapMode mode：内存映像文件访问的方式，共三种：MapMode.READ_ONLY：只读，试图修改得到的缓冲区将导致抛出异常。MapMode.READ_WRITE：读/写，对得到的缓冲区的更改最终将写入文件；但该更改对映射到同一文件的其他程序不一定是可见的。MapMode.PRIVATE：私用，可读可写,但是修改的内容不会写入文件，只是buffer自身的改变，这种能力称之为”copy on write”。
*
* position：文件映射时的起始位置。
* allocationGranularity：Memory allocation size for mapping buffers，通过native函数initIDs初始化。
*/</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<ul>
<li><p>capacity(缓冲区数组的总长度 即可以容纳的最大数据量；在缓冲区创建时被设定并且不能改变)。capacity作为一个内存块，Buffer有一个固定的大小值，也叫“capacity”.你只能往里写capacity个byte、long，char等类型。一旦Buffer满了，需要将其清空（通过读数据或者清除数据）才能继续写数据往里写数据。</p>
</li>
<li><p>position(下一个要操作的数据元素的位置,下一个要被读或写的元素的索引，每次读写缓冲区数据时都会改变改值，为下次读写作准备),当你写数据到Buffer中时，position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后，position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.当读取数据时，也是从某个特定位置读。当将Buffer从写模式切换到读模式，position会被重置为0. 当从Buffer的position处读取数据时，position向前移动到下一个可读的位置。</p>
</li>
<li><p>limit(缓冲区数组中不可操作的下一个元素的位置：limit&lt;&#x3D;capacity表示缓冲区的当前终点，不能对缓冲区超过极限的位置进行读写操作。且极限是可以修改的)。limit在写模式下，Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下，limit等于Buffer的capacity。当切换Buffer到读模式时，limit表示你最多能读到多少数据。因此，当切换Buffer到读模式时，limit会被设置成写模式下的position值。换句话说，你能读到之前写入的所有数据（limit被设置成已写数据的数量，这个值在写模式下就是position）</p>
</li>
<li><p>mark(标记，调用mark()来设置mark&#x3D;position，再调用reset()可以让position恢复到标记的位置)<br>mark &lt;&#x3D; position &lt;&#x3D; limit &lt;&#x3D; capacity。position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer处在什么模式，capacity的含义总是一样的。</p>
</li>
</ul>
<p><strong>Buffer常用方法</strong></p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token keyword">int</span> capacity<span class="token punctuation">)</span><span class="token comment">//从堆空间中分配一个容量大小为capacity的byte数组作为缓冲区的byte数据存储器</span>
<span class="token function">allocateDirect</span><span class="token punctuation">(</span><span class="token keyword">int</span> capacity<span class="token punctuation">)</span><span class="token comment">//是不使用JVM堆栈而是通过操作系统来创建内存块用作缓冲区，它与当前操作系统能够更好的耦合，因此能进一步提高I/O操作速度。但是分配直接缓冲区的系统开销很大，因此只有在缓冲区较大并长期存在，或者需要经常重用时，才使用这种缓冲区</span>
<span class="token function">wrap</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> array<span class="token punctuation">)</span><span class="token comment">//这个缓冲区的数据会存放在byte数组中，bytes数组或buff缓冲区任何一方中数据的改动都会影响另一方。其实ByteBuffer底层本来就有一个bytes数组负责来保存buffer缓冲区中的数据，通过allocate方法系统会帮你构造一个byte数组</span>
<span class="token function">wrap</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> array<span class="token punctuation">,</span> <span class="token keyword">int</span> offset<span class="token punctuation">,</span> <span class="token keyword">int</span> length<span class="token punctuation">)</span> <span class="token comment">//在上一个方法的基础上可以指定偏移量和长度，这个offset也就是包装后byteBuffer的position，而length呢就是limit-position的大小，从而我们可以得到limit的位置为length+position(offset)</span>
<span class="token function">limit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">limit</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span>等<span class="token comment">//其中读取和设置这4个属性的方法的命名和jQuery中的val(),val(10)类似，一个负责get，一个负责set</span>
<span class="token function">reset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//把position设置成mark的值，相当于之前做过一个标记，现在要退回到之前标记的地方</span>
<span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//position = 0;limit = capacity;mark = -1;有点初始化的味道，但是并不影响底层byte数组的内容</span>
<span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//limit = position;position = 0;mark = -1;翻转，也就是让flip之后的position到limit这块区域变成之前的0到position这块，翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态 一般在从Buffer读出数据前调用。</span>
<span class="token function">rewind</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//把position设为0，mark设为-1，不改变limit的值 一般在把数据重写入Buffer前调用 或者重新读取</span>
<span class="token function">remaining</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//return limit - position;返回limit和position之间相对位置差</span>
<span class="token function">hasRemaining</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//return position &lt; limit返回是否还有未读内容</span>
<span class="token function">compact</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//把从position到limit中的内容移到0到limit-position的区域内，position和limit的取值也分别变成limit-position、capacity。如果先将positon设置到limit，再compact，那么相当于clear()</span>
<span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//相对读，从position位置读取一个byte，并将position+1，为下次读写作准备</span>
<span class="token function">get</span><span class="token punctuation">(</span><span class="token keyword">int</span> index<span class="token punctuation">)</span><span class="token comment">//绝对读，读取byteBuffer底层的bytes中下标为index的byte，不改变position</span>
<span class="token function">get</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> dst<span class="token punctuation">,</span> <span class="token keyword">int</span> offset<span class="token punctuation">,</span> <span class="token keyword">int</span> length<span class="token punctuation">)</span><span class="token comment">//从position位置开始相对读，读length个byte，并写入dst下标从offset到offset+length的区域</span>
<span class="token function">put</span><span class="token punctuation">(</span><span class="token keyword">byte</span> b<span class="token punctuation">)</span><span class="token comment">//相对写，向position的位置写入一个byte，并将postion+1，为下次读写作准备</span>
<span class="token function">put</span><span class="token punctuation">(</span><span class="token keyword">int</span> index<span class="token punctuation">,</span> <span class="token keyword">byte</span> b<span class="token punctuation">)</span><span class="token comment">//绝对写，向byteBuffer底层的bytes中下标为index的位置插入byte b，不改变position</span>
<span class="token function">put</span><span class="token punctuation">(</span><span class="token class-name">ByteBuffer</span> src<span class="token punctuation">)</span><span class="token comment">//用相对写，把src中可读的部分（也就是position到limit）写入此byteBuffer</span>
<span class="token function">put</span><span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> src<span class="token punctuation">,</span> <span class="token keyword">int</span> offset<span class="token punctuation">,</span> <span class="token keyword">int</span> length<span class="token punctuation">)</span><span class="token comment">//从src数组中的offset到offset+length区域读取数据并使用相对写写入此byteBuffer</span>
<span class="token class-name">ByteOrder</span> <span class="token function">order</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//检索此缓冲区的字节顺序。</span>
<span class="token class-name">ByteBuffer</span> <span class="token function">order</span><span class="token punctuation">(</span><span class="token class-name">ByteOrder</span> bo<span class="token punctuation">)</span><span class="token comment">//修改缓冲区的字节顺序。</span>
<span class="token class-name">ByteBuffer</span> <span class="token function">putInt</span><span class="token punctuation">(</span><span class="token keyword">int</span> value<span class="token punctuation">)</span><span class="token comment">//编写int值的相对put方法（可选操作） 。以当前字节顺序将包含给定int值的四个字节写入当前位置的缓冲区，然后将位置递增四。</span>
<span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token function">array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//返回支持此缓冲区的字节数组（可选操作） 。对此缓冲区内容的修改将导致返回的数组的内容被修改，反之亦然。在调用此方法之前调用hasArray方法，以确保此缓冲区具有可访问的后台阵列。</span>
<span class="token class-name">Buffer</span><span class="token punctuation">.</span><span class="token function">mark</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">//通过调用Buffer.mark()方法，可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position</span>
<span class="token comment">//可以使用equals()和compareTo()方法两个Buffer。</span>
<span class="token function">equals</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">//当满足下列条件时，表示两个Buffer相等：有相同的类型（byte、char、int等）。Buffer中剩余的byte、char等的个数相等。Buffer中所有剩余的byte、char等都相同。如你所见，equals只是比较Buffer的一部分，不是每一个在它里面的元素都比较。实际上，它只比较Buffer中的剩余元素。</span>
<span class="token function">compareTo</span><span class="token punctuation">(</span><span class="token punctuation">)</span>方法
<span class="token comment">//compareTo()方法比较两个Buffer的剩余元素(byte、char等)， 如果满足下列条件，则认为一个Buffer“小于”另一个Buffer：第一个不相等的元素小于另一个Buffer中对应的元素 。所有元素都相等，但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>


<h3 id="Channel"><a href="#Channel" class="headerlink" title="Channel"></a>Channel</h3><p>NIO中的Channel的主要实现有：FileChannel(从文件中读写数据) 、DatagramChannel(通过UDP读写网络中的数据)、SocketChannel(通过TCP读写网络中的数据)、ServerSocketChannel(可以监听新进来的TCP连接，像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel)</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**
* NIO读取文件 RandomAccessFile进行操作，也可以通FileInputStream.getChannel()获取channel进行操作
*/</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">newIORead</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token class-name">RandomAccessFile</span> aFile <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        aFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token string">"src/nio.txt"</span><span class="token punctuation">,</span> <span class="token string">"rw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">FileChannel</span> fileChannel <span class="token operator">=</span> aFile<span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// buffer分配空间 根据Buffer实现类表明分配时的单位 下面代表分配1024个字节 CharBuffer就代表1024个字符</span>
        <span class="token class-name">ByteBuffer</span> buf <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token number">1024</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 通道必须结合Buffer使用，不能直接向通道中读/写数据，</span>
        <span class="token comment">// read()表示读channel数据写入到buffer，write()表示读取buffer数据写入到channel。</span>
        <span class="token keyword">int</span> bytesRead <span class="token operator">=</span> fileChannel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span>bytesRead <span class="token operator">!=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token comment">// 在读模式下，可以读取之前写入到buffer的所有数据,调用flip()方法,position设回0，并将limit设成之前的position的值</span>
            <span class="token comment">// 数据就是从position到limit的数据</span>
            <span class="token comment">// capacity(缓冲区数组的总长度),position(下一个要操作的数据元素的位置),limit(缓冲区数组中不可操作的下一个元素的位置：limit&lt;=capacity),mark(用于记录当前position的前一个位置或者默认是-1)</span>
            buf<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment">// hasRemaining()用于判断当前位置(position)和限制(limit)之间是否有任何元素</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span>buf<span class="token punctuation">.</span><span class="token function">hasRemaining</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> buf<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
            <span class="token comment">// clear()方法：position将被设回0，limit设置成capacity，换句话说，Buffer被清空了，</span>
            <span class="token comment">// 其实Buffer中的数据并未被清除，只是这些标记告诉我们可以从哪里开始往Buffer里写数据。</span>
            <span class="token comment">// 如果Buffer中有一些未读的数据，调用clear()方法，数据将“被遗忘”，意味着不再有任何标记会告诉你哪些数据被读过，哪些还没有。</span>
            <span class="token comment">// 如果Buffer中仍有未读的数据，且后续还需要这些数据，但是此时想要先写些数据，那么使用compact()方法。</span>
            <span class="token comment">// compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样，设置成capacity。</span>
            <span class="token comment">// 现在Buffer准备好写数据了，但是不会覆盖未读的数据。</span>
            buf<span class="token punctuation">.</span><span class="token function">compact</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            bytesRead <span class="token operator">=</span> fileChannel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">finally</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>aFile <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                aFile<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * NIO写数据 通过FileChannel写入数据
 */</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token class-name">FileChannelOnWrite</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">RandomAccessFile</span> accessFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token string">"D://file1.txt"</span><span class="token punctuation">,</span> <span class="token string">"rw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">FileChannel</span> fc <span class="token operator">=</span> accessFile<span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> bytes <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"write to file1.txt"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 获得ByteBuffer的实例 类似allocate(int capacity)方法</span>
        <span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">wrap</span><span class="token punctuation">(</span>bytes<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 读取缓冲区数据写入到通道。</span>
        fc<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 清空缓存区 使得缓存区可以继续写入数据</span>
        byteBuffer<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 缓存区写入内容</span>
        byteBuffer<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">",a good boy"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 写模式转化读模式</span>
        byteBuffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 读取缓冲区数据写入到通道。</span>
        fc<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
        fc<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        accessFile<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中
 * （译者注：这个方法在JDK文档中的解释为将字节从给定的可读取字节通道传输到此通道的文件中）。
 */</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testTransferFrom</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">RandomAccessFile</span> fromFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token string">"D://file1.txt"</span><span class="token punctuation">,</span> <span class="token string">"rw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">FileChannel</span> fromChannel <span class="token operator">=</span> fromFile<span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">RandomAccessFile</span> toFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token string">"D://file2.txt"</span><span class="token punctuation">,</span> <span class="token string">"rw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">FileChannel</span> toChannel <span class="token operator">=</span> toFile<span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">long</span> position <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
        <span class="token keyword">long</span> count <span class="token operator">=</span> fromChannel<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        toChannel<span class="token punctuation">.</span><span class="token function">transferFrom</span><span class="token punctuation">(</span>fromChannel<span class="token punctuation">,</span> position<span class="token punctuation">,</span> count<span class="token punctuation">)</span><span class="token punctuation">;</span>

        fromFile<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        toFile<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * transferTo()方法将数据从FileChannel传输到其他的channel中
 */</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">testTransferTo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">RandomAccessFile</span> fromFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token string">"D://file1.txt"</span><span class="token punctuation">,</span> <span class="token string">"rw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">FileChannel</span> fromChannel <span class="token operator">=</span> fromFile<span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">RandomAccessFile</span> toFile <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RandomAccessFile</span><span class="token punctuation">(</span><span class="token string">"D://file3.txt"</span><span class="token punctuation">,</span> <span class="token string">"rw"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">FileChannel</span> toChannel <span class="token operator">=</span> toFile<span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">long</span> position <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
        <span class="token keyword">long</span> count <span class="token operator">=</span> fromChannel<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        fromChannel<span class="token punctuation">.</span><span class="token function">transferTo</span><span class="token punctuation">(</span>position<span class="token punctuation">,</span> count<span class="token punctuation">,</span> toChannel<span class="token punctuation">)</span><span class="token punctuation">;</span>
        fromFile<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        toFile<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">//-----------------------nio socket-------------------------------------</span>

<span class="token comment">/**
 * NIO新建socket client
 */</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">newIOSocketClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token class-name">ByteBuffer</span> buffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token number">1024</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 通过 ServerSocketChannel.open()方法来创建一个新的ServerSocketChannel对象，</span>
        <span class="token comment">// 该对象关联了一个未绑定ServerSocket的通道.通过调用该对象上的socket()方法可以获取与之关联的ServerSocket。</span>
        socketChannel <span class="token operator">=</span> <span class="token class-name">SocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        socketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        socketChannel<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token string">"10.10.195.115"</span><span class="token punctuation">,</span> <span class="token number">8080</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 为了确定连接是否建立，可以调用finishConnect()的方法。</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>socketChannel<span class="token punctuation">.</span><span class="token function">finishConnect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token class-name">TimeUnit</span><span class="token punctuation">.</span><span class="token constant">SECONDS</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token class-name">String</span> info <span class="token operator">=</span> <span class="token string">"I'm "</span> <span class="token operator">+</span> i<span class="token operator">++</span> <span class="token operator">+</span> <span class="token string">"-th information from client"</span><span class="token punctuation">;</span>
                buffer<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                buffer<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>info<span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                buffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">while</span> <span class="token punctuation">(</span>buffer<span class="token punctuation">.</span><span class="token function">hasRemaining</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    socketChannel<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">finally</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>socketChannel <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                socketChannel<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * NIO socket server
 * 
 * @throws Exception
 */</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token class-name">NIOSocketServer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">&#123;</span>
    <span class="token class-name">ServerSocketChannel</span> socketChannel <span class="token operator">=</span> <span class="token class-name">ServerSocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 非阻塞模式</span>
    socketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// socketChannel.socket().bind(new InetSocketAddress(9999));jdk 1.7之前</span>
    socketChannel<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token number">9999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token number">1024</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 通过 ServerSocketChannel.accept() 方法监听新进来的连接。</span>
        <span class="token comment">// 在阻塞模式下当 accept()方法返回的时候,它返回一个包含新进来的连接的SocketChannel，否则accept()方法会一直阻塞到有新连接到达。</span>
        <span class="token comment">// 在非阻塞模式下，在没有新连接的情况下，accept()会立即返回null，该模式下通常不会仅仅只监听一个连接,因此需在while循环中调用</span>
        <span class="token comment">// accept()方法.</span>
        <span class="token class-name">SocketChannel</span> channel <span class="token operator">=</span> socketChannel<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>channel <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token class-name">InetSocketAddress</span> remoteAddress <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">InetSocketAddress</span><span class="token punctuation">)</span> channel<span class="token punctuation">.</span><span class="token function">getRemoteAddress</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>remoteAddress<span class="token punctuation">.</span><span class="token function">getAddress</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>remoteAddress<span class="token punctuation">.</span><span class="token function">getPort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            channel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
            byteBuffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span>byteBuffer<span class="token punctuation">.</span><span class="token function">hasRemaining</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> byteBuffer<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * UDP发送方
 * 
 * @throws Exception
 */</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token class-name">DatagramChannelsend</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 通过DatagramChannel的open()方法来创建。需要注意DatagramChannel的open()方法只是打开获得通道，</span>
    <span class="token comment">// 但此时尚未连接。尽管DatagramChannel无需建立连接（远端连接），但仍然可以通过isConnect()检测当前的channel是否声明了远端连接地址。</span>
    <span class="token class-name">DatagramChannel</span> channel <span class="token operator">=</span> <span class="token class-name">DatagramChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">wrap</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"i 'm client"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 通过send()方法将ByteBuffer中的内容发送到指定的SocketAddress对象所描述的地址。在阻塞模式下，调用线程会被阻塞至有数据包被加入传输队列。</span>
    <span class="token comment">// 非阻塞模式下，如果发送内容为空则返回0，否则返回发送的字节数。发送数据报是一个全有或全无(all-or-nothing)的行为。</span>
    <span class="token comment">// 如果传输队列没有足够空间来承载整个数据报，那么什么内容都不会被发送。</span>
    <span class="token comment">// 请注意send()方法返回的非零值并不表示数据报到达了目的地，仅代表数据报被成功加到本地网络层的传输队列。</span>
    <span class="token comment">// 此外，传输过程中的协议可能将数据报分解成碎片，被分解的数据报在目的地会被重新组合起来，接收者将看不到碎片。</span>
    <span class="token comment">// 但是，如果有一个碎片不能按时到达，那么整个数据报将被丢弃。分解有助于发送大数据报，但也会会造成较高的丢包率。</span>
    <span class="token keyword">int</span> bytesSent <span class="token operator">=</span> channel<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token string">"127.0.0.1"</span><span class="token punctuation">,</span> <span class="token number">9999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"bytesSent="</span><span class="token operator">+</span>bytesSent<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * UDP接收方
 */</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">receiveData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">&#123;</span>
    <span class="token class-name">DatagramChannel</span> channel <span class="token operator">=</span> <span class="token class-name">DatagramChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    channel<span class="token punctuation">.</span><span class="token function">socket</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token number">9999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token number">1024</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    byteBuffer<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 通过receive()方法接受DatagramChannel中数据。从该方法将传入的数据报的数据将被复制到ByteBuffer中，</span>
    <span class="token comment">// 同时返回一个SocketAddress对象以指出数据来源。在阻塞模式下，receive()将会阻塞至有数据包到来，</span>
    <span class="token comment">// 非阻塞模式下，如果没有可接受的包则返回null。如果包内的数据大小超过缓冲区容量时，多出的数据会被悄悄抛弃</span>
    <span class="token class-name">SocketAddress</span> address <span class="token operator">=</span> channel<span class="token punctuation">.</span><span class="token function">receive</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// receive data</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>address<span class="token punctuation">)</span><span class="token punctuation">;</span>
    byteBuffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span>byteBuffer<span class="token punctuation">.</span><span class="token function">hasRemaining</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> byteBuffer<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>**Channel常用方法 **</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">int</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token class-name">ByteBuffer</span> dst<span class="token punctuation">)</span> <span class="token comment">//从Channel到中读取数据到ByteBuffer </span>
<span class="token keyword">long</span> <span class="token function">read</span><span class="token punctuation">(</span><span class="token class-name">ByteBuffer</span><span class="token punctuation">[</span><span class="token punctuation">]</span> dsts<span class="token punctuation">)</span> <span class="token comment">//将Channel到中的数据“分散”到ByteBuffer[] </span>
<span class="token keyword">int</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token class-name">ByteBuffer</span> src<span class="token punctuation">)</span> <span class="token comment">//将ByteBuffer到中的数据写入到Channel </span>
<span class="token keyword">long</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token class-name">ByteBuffer</span><span class="token punctuation">[</span><span class="token punctuation">]</span> srcs<span class="token punctuation">)</span> <span class="token comment">//将ByteBuffer[]到中的数据“聚集”到Channel </span>
<span class="token keyword">long</span> <span class="token function">position</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//返回此通道的文件位置 </span>
<span class="token class-name">FileChannel</span> <span class="token function">position</span><span class="token punctuation">(</span><span class="token keyword">long</span> p<span class="token punctuation">)</span> <span class="token comment">//设置此通道的文件位置 </span>
<span class="token keyword">long</span> <span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//返回此通道的文件的当前大小 </span>
<span class="token class-name">FileChannel</span> <span class="token function">truncate</span><span class="token punctuation">(</span><span class="token keyword">long</span> s<span class="token punctuation">)</span> <span class="token comment">//将此通道的文件截取为给定大小 </span>
<span class="token keyword">void</span> <span class="token function">force</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> metaData<span class="token punctuation">)</span> <span class="token comment">//强制将所有对此通道的文件更新写入到存储设备中</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h3 id="Selector"><a href="#Selector" class="headerlink" title="Selector"></a>Selector</h3><p>Selector(选择区)用于监听多个通道的事件（比如：连接打开，数据到达）因此，单个线程可以监听多个数据通道。</p>
<p>Selector的创建</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Selector</span> selector <span class="token operator">=</span> <span class="token class-name">Selector</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>为了将Channel和Selector配合使用，必须将Channel注册到Selector上，通过SelectableChannel.register()方法来实现，沿用nio创建socket</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">ServerSocketChannel</span> channel <span class="token operator">=</span> <span class="token class-name">ServerSocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
channel<span class="token punctuation">.</span><span class="token function">socket</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token constant">PORT</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
channel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">SelectionKey</span> key <span class="token operator">=</span> channel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token constant">OP_ACCEPT</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<p>与Selector一起使用时，Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用，因为FileChannel不能切换到非阻塞模式。而套接字通道都可以。<br>注意register()方法的第二个参数。这是一个“interest集合”，意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件：</p>
<ol>
<li>Connect连接 </li>
<li>Accept接收 </li>
<li>Read读</li>
<li>Write写</li>
</ol>
<p>这四种事件用SelectionKey的四个常量来表示：1.SelectionKey.OP_CONNECT 2. SelectionKey.OP_ACCEPT 3. SelectionKey.OP_READ 4.SelectionKey.OP_WRITE</p>
<p><strong>SelectionKey</strong><br>一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。<br>当向Selector注册Channel时，register()方法会返回一个SelectionKey对象。这个对象包含了一些你感兴趣的属性<br>interest集合：就像向Selector注册通道一节中所描述的，interest集合是你所选择的感兴趣的事件集合。可以通过SelectionKey读写interest集合。<br>ready集合：通道已经准备就绪的操作的集合。在一次选择(Selection)之后，你会首先访问这个ready set。可以这样访问ready集合： int readySet &#x3D; selectionKey.readyOps();<br>可以用像检测interest集合那样的方法，来检测channel中什么事件或操作已经就绪。但是，也可以使用以下四个方法，它们都会返回一个布尔类型：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java">selectionKey<span class="token punctuation">.</span><span class="token function">isAcceptable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
selectionKey<span class="token punctuation">.</span><span class="token function">isConnectable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
selectionKey<span class="token punctuation">.</span><span class="token function">isReadable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
selectionKey<span class="token punctuation">.</span><span class="token function">isWritable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<p>从SelectionKey访问Channel和Selector很简单:</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Channel</span> channel <span class="token operator">=</span> selectionKey<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token class-name">Selector</span> selector <span class="token operator">=</span> selectionKey<span class="token punctuation">.</span><span class="token function">selector</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>可以将一个对象或者更多信息附着到SelectionKey上，这样就能方便的识别某个给定的通道。例如，可以附加与通道一起使用的Buffer，或是包含聚集数据的某个对象。使用方法：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java">selectionKey<span class="token punctuation">.</span><span class="token function">attach</span><span class="token punctuation">(</span>theObject<span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token class-name">Object</span> attachedObj <span class="token operator">=</span> selectionKey<span class="token punctuation">.</span><span class="token function">attachment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>还可以在用register()方法向Selector注册Channel的时候附加对象。如： </p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">SelectionKey</span> key <span class="token operator">=</span> channel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token constant">OP_READ</span><span class="token punctuation">,</span> theObject<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>

<p><strong>SelectionKey常用方法</strong></p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java">key<span class="token punctuation">.</span><span class="token function">attachment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//返回SelectionKey的attachment，attachment可以在注册channel的时候指定。</span>
key<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//返回该SelectionKey对应的channel。</span>
key<span class="token punctuation">.</span><span class="token function">selector</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//返回该SelectionKey对应的Selector。</span>
key<span class="token punctuation">.</span><span class="token function">interestOps</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//返回代表需要Selector监控的IO操作的bit mask</span>
key<span class="token punctuation">.</span><span class="token function">readyOps</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//返回一个bit mask，代表在相应channel上可以进行的IO操作。</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>通过Selector选择通道一旦向Selector注册了一或多个通道，就可以调用几个重载的select()方法。这些方法返回你所感兴趣的事件（如连接、接受、读或写）已经准备就绪的那些通道。</p>
<p><strong>select()方法</strong></p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">int</span> <span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//阻塞到至少有一个通道在你注册的事件上就绪了</span>
<span class="token keyword">int</span> <span class="token function">select</span><span class="token punctuation">(</span><span class="token keyword">long</span> timeout<span class="token punctuation">)</span> <span class="token comment">//和select()一样，除了最长会阻塞timeout毫秒(参数)。</span>
<span class="token keyword">int</span> <span class="token function">selectNow</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">//不会阻塞，不管什么通道就绪都立刻返回（译者注：此方法执行非阻塞的选择操作。如果自从前一次选择操作后，没有通道变成可选择的，则此方法直接返回零。</span>
<span class="token comment">//select()方法返回的int值表示有多少通道已经就绪。亦即自上次调用select()方法后有多少通道变成就绪状态。如果调用select()方法，有一个通道变成就绪状态，返回了1，若再次调用select()方法，如果另一个通道就绪了，它会再次返回1。如果对第一个就绪的channel没有做任何操作，现在就有两个就绪的通道，但在每次select()方法调用之间，只有一个通道就绪了。一旦调用了select()方法，并且返回值表明有一个或更多个通道就绪了，然后可以通过调用selector的selectedKeys()方法，Set selectedKeys = selector.selectedKeys();当向Selector注册Channel时，Channel.register()方法会返回一个SelectionKey对象。这个对象代表了注册到该Selector的通道。注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时，Selector会再次将其放入已选择键集中。</span>
<span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">//方法返回的通道需要转型成你要处理的类型，如ServerSocketChannel或SocketChannel等。。</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">ServerSelector</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token constant">BUF_SIZE</span> <span class="token operator">=</span> <span class="token number">1024</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token constant">PORT</span> <span class="token operator">=</span> <span class="token number">8080</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token constant">TIMEOUT</span> <span class="token operator">=</span> <span class="token number">3000</span><span class="token punctuation">;</span>

    <span class="token comment">// public static void main(String[] args)&#123;</span>
    <span class="token comment">//     selector();</span>
    <span class="token comment">// &#125;</span>

    <span class="token comment">/**
     * 接收就绪处理
     * @param key
     * @throws Exception
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">handleAccept</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span> key<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">ServerSocketChannel</span> serverSocketChannel <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">ServerSocketChannel</span><span class="token punctuation">)</span> key<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> serverSocketChannel<span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        socketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        socketChannel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>key<span class="token punctuation">.</span><span class="token function">selector</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token constant">OP_READ</span><span class="token punctuation">,</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocateDirect</span><span class="token punctuation">(</span><span class="token constant">BUF_SIZE</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 读就绪处理
     * @param key
     * @throws Exception
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">handleRead</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span> key<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">SocketChannel</span><span class="token punctuation">)</span> key<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">ByteBuffer</span> buf <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">ByteBuffer</span><span class="token punctuation">)</span> key<span class="token punctuation">.</span><span class="token function">attachment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">long</span> bytesRead <span class="token operator">=</span> socketChannel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span>bytesRead <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            buf<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span>buf<span class="token punctuation">.</span><span class="token function">hasRemaining</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span> buf<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
            <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            buf<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            bytesRead <span class="token operator">=</span> socketChannel<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>bytesRead <span class="token operator">==</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            socketChannel<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 写就绪处理
     * @param key
     * @throws Exception
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">handleWrite</span><span class="token punctuation">(</span><span class="token class-name">SelectionKey</span> key<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">ByteBuffer</span> buf <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">ByteBuffer</span><span class="token punctuation">)</span> key<span class="token punctuation">.</span><span class="token function">attachment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        buf<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">SocketChannel</span><span class="token punctuation">)</span> key<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span>buf<span class="token punctuation">.</span><span class="token function">hasRemaining</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            socketChannel<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        buf<span class="token punctuation">.</span><span class="token function">compact</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 注册选择器
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">selector</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">Selector</span> selector <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
        <span class="token class-name">ServerSocketChannel</span> serverSocketChannel <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
            <span class="token comment">// 打开选择器</span>
            selector <span class="token operator">=</span> <span class="token class-name">Selector</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment">// 打开ServerSocketChannel通道</span>
            serverSocketChannel <span class="token operator">=</span> <span class="token class-name">ServerSocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            serverSocketChannel<span class="token punctuation">.</span><span class="token function">socket</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span><span class="token constant">PORT</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment">// 设置非阻塞模式</span>
            serverSocketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment">// 注册 监听接收</span>
            serverSocketChannel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token constant">OP_ACCEPT</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>selector<span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token constant">TIMEOUT</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"未检测到有通道就绪"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token keyword">continue</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
                <span class="token class-name">Iterator</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SelectionKey</span><span class="token punctuation">></span></span> iter <span class="token operator">=</span> selector<span class="token punctuation">.</span><span class="token function">selectedKeys</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">while</span> <span class="token punctuation">(</span>iter<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    <span class="token class-name">SelectionKey</span> key <span class="token operator">=</span> iter<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span>key<span class="token punctuation">.</span><span class="token function">isAcceptable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                        <span class="token comment">// 接收就绪</span>
                        <span class="token function">handleAccept</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">&#125;</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span>key<span class="token punctuation">.</span><span class="token function">isReadable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                        <span class="token comment">// 读就绪</span>
                        <span class="token function">handleRead</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">&#125;</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span>key<span class="token punctuation">.</span><span class="token function">isWritable</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> key<span class="token punctuation">.</span><span class="token function">isValid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                        <span class="token comment">// 写就绪</span>
                        <span class="token function">handleWrite</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">&#125;</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span>key<span class="token punctuation">.</span><span class="token function">isConnectable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                        <span class="token comment">// 连接就绪</span>
                        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"isConnectable = true"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">&#125;</span>
                    iter<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span> <span class="token keyword">finally</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>selector <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    selector<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>serverSocketChannel <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    serverSocketChannel<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
            <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">ClientSelector</span> <span class="token punctuation">&#123;</span>

    <span class="token comment">/*标识数字*/</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">int</span> flag <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token comment">/*缓冲区大小*/</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">int</span> <span class="token constant">BLOCK</span> <span class="token operator">=</span> <span class="token number">4096</span><span class="token punctuation">;</span>
    <span class="token comment">/*接受数据缓冲区*/</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">ByteBuffer</span> sendbuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token constant">BLOCK</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">/*发送数据缓冲区*/</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">ByteBuffer</span> receivebuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token constant">BLOCK</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">/*服务器端地址*/</span>
    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">static</span> <span class="token class-name">InetSocketAddress</span> <span class="token constant">SERVER_ADDRESS</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">InetSocketAddress</span><span class="token punctuation">(</span>
            <span class="token string">"localhost"</span><span class="token punctuation">,</span> <span class="token number">8888</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// public static void main(String[] args) &#123;</span>
    <span class="token comment">//     selector();</span>
    <span class="token comment">// &#125;</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">selector</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span><span class="token punctuation">&#123;</span>
        <span class="token comment">// 打开socket通道</span>
        <span class="token class-name">SocketChannel</span> socketChannel <span class="token operator">=</span> <span class="token class-name">SocketChannel</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 设置为非阻塞方式</span>
        socketChannel<span class="token punctuation">.</span><span class="token function">configureBlocking</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 打开选择器</span>
        <span class="token class-name">Selector</span> selector <span class="token operator">=</span> <span class="token class-name">Selector</span><span class="token punctuation">.</span><span class="token keyword">open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 注册连接服务端socket动作</span>
        socketChannel<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token constant">OP_CONNECT</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 连接</span>
        socketChannel<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span><span class="token constant">SERVER_ADDRESS</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">int</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token comment">//选择一组键，其相应的通道已为 I/O 操作准备就绪。</span>
            <span class="token comment">//此方法执行处于阻塞模式的选择操作。</span>
            <span class="token keyword">int</span> selctorCount <span class="token operator">=</span> selector<span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">if</span><span class="token punctuation">(</span>selctorCount <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">continue</span><span class="token punctuation">;</span>
            <span class="token comment">//返回此选择器的已选择键集。</span>
            <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SelectionKey</span><span class="token punctuation">></span></span> selectionKeys <span class="token operator">=</span> selector<span class="token punctuation">.</span><span class="token function">selectedKeys</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment">//System.out.println(selectionKeys.size());</span>
            <span class="token class-name">Iterator</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SelectionKey</span><span class="token punctuation">></span></span> iterator <span class="token operator">=</span> selectionKeys<span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span>iterator<span class="token punctuation">.</span><span class="token function">hasNext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token class-name">SelectionKey</span> selectionKey <span class="token operator">=</span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>selectionKey<span class="token punctuation">.</span><span class="token function">isConnectable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"client connect"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token class-name">SocketChannel</span> client <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">SocketChannel</span><span class="token punctuation">)</span> selectionKey<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token comment">// 判断此通道上是否正在进行连接操作。</span>
                    <span class="token comment">// 完成套接字通道的连接过程。</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span>client<span class="token punctuation">.</span><span class="token function">isConnectionPending</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                        client<span class="token punctuation">.</span><span class="token function">finishConnect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"完成连接!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                        sendbuffer<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                        sendbuffer<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"Hello,Server"</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                        sendbuffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                        client<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>sendbuffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">&#125;</span>
                    client<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token constant">OP_READ</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>selectionKey<span class="token punctuation">.</span><span class="token function">isReadable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    <span class="token class-name">SocketChannel</span> client <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">SocketChannel</span><span class="token punctuation">)</span> selectionKey<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token comment">//将缓冲区清空以备下次读取</span>
                    receivebuffer<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token comment">//读取服务器发送来的数据到缓冲区中</span>
                    count <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>receivebuffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token keyword">if</span><span class="token punctuation">(</span>count <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
                        <span class="token class-name">String</span> receiveText <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span> receivebuffer<span class="token punctuation">.</span><span class="token function">array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span>count<span class="token punctuation">)</span><span class="token punctuation">;</span>
                        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"客户端接受服务器端数据--:"</span><span class="token operator">+</span>receiveText<span class="token punctuation">)</span><span class="token punctuation">;</span>
                        client<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token constant">OP_WRITE</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">&#125;</span>

                <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>selectionKey<span class="token punctuation">.</span><span class="token function">isWritable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    sendbuffer<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token class-name">SocketChannel</span> client <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">SocketChannel</span><span class="token punctuation">)</span> selectionKey<span class="token punctuation">.</span><span class="token function">channel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token class-name">String</span> sendText <span class="token operator">=</span> <span class="token string">"message from client--"</span> <span class="token operator">+</span> <span class="token punctuation">(</span>flag<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    sendbuffer<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>sendText<span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token comment">//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位</span>
                    sendbuffer<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    client<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>sendbuffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"客户端向服务器端发送数据--："</span><span class="token operator">+</span>sendText<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    client<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span>selector<span class="token punctuation">,</span> <span class="token class-name">SelectionKey</span><span class="token punctuation">.</span><span class="token constant">OP_READ</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
            <span class="token punctuation">&#125;</span>
            selectionKeys<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<blockquote>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/snailclimb/p/9086334.html">NIO之Selector选择器</a></p>
</blockquote>
<h3 id="Netty"><a href="#Netty" class="headerlink" title="Netty"></a>Netty</h3><table>
<thead>
<tr>
<th align="center"><a target="_blank" rel="noopener" href="https://github.com/xmxe/demo/tree/master/study-demo/src/main/java/com/xmxe/jdkfeature/nio/netty">Netty demo</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/uMak8HmIyl78hcEUtovAvg">让Netty“榨干”你的CPU</a></th>
<th align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/W-KZFn40FnwIksP1zQP4RQ">Netty简易实战，傻瓜都能看懂！</a></th>
</tr>
</thead>
<tbody><tr>
<td align="center"><a target="_blank" rel="noopener" href="https://blog.csdn.net/forezp/article/details/88414741">Java NIO？看这一篇就够了！</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://ifeve.com/overview/">NIO系列教程</a></td>
<td align="center"><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/jpWIhws9b2ASD5k6A1pBvg">Netty底层的IO模型是什么？</a></td>
</tr>
</tbody></table>

                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/blog/about" rel="external nofollow noreferrer">xmxe</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://xmxe.github.io/blog/posts/c98fd7c2aa89/">https://xmxe.github.io/blog/posts/c98fd7c2aa89/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/blog/about" target="_blank">xmxe</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            <span class="chip bg-color">无标签</span>
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/blog/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="qq,qzone,wechat,weibo,douban" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/blog/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        
                            <img src="/blog/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码">
                        
                    </div>
                    <div id="wechat">
                        
                            <img src="/blog/medias/reward/wechat.jpg" class="reward-img" alt="微信打赏二维码">
                        
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/blog/posts/f55fdb8ec84c/">
                    <div class="card-image">
                        
                        <img src="https://pic1.zhimg.com/v2-e69bde8250c0acfa3082076b373d6998_1440w.jpg" class="responsive-img" alt="常见的限流算法">
                        
                        <span class="card-title">常见的限流算法</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            常见限流算法有哪些？简单介绍4种非常好理解并且容易实现的限流算法！

图片来源于InfoQ的一篇文章《分布式服务限流实战，已经为你排好坑了》。

固定窗口计数器算法固定窗口其实就是时间窗口。固定窗口计数器算法规定了我们单位时间处理的请求数量
                        

                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <!--
                            <i class="far fa-clock fa-fw icon-date"></i>2022-11-15
                            -->

                            
                                <i class="fas fa-user fa-fw"></i>
                                <a href="/blog/about" >
                                    xmxe
                                </a>
                            
                        </span>
                        <span class="publish-author">
                            
                                
                                
                                <a href="/blog/tags/%E4%BB%A3%E7%A0%81%E5%AE%9E%E6%88%98/">
                                    <span class="chip bg-color">代码实战</span>
                                </a>
                                
                                
                            

                        </span>
                    </div>
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/blog/posts/32a593b381de/">
                    <div class="card-image">
                        
                        <img src="https://pica.zhimg.com/v2-6e9fdfa6a51b5fd85c96c25a1ba0f289_1440w.jpg" class="responsive-img" alt="计算机网络">
                        
                        <span class="card-title">计算机网络</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            TCP与UDPTCP与UDP的区别
是否面向连接：UDP在传送数据之前不需要先建立连接。而TCP提供面向连接的服务，在传送数据之前必须先建立连接，数据传送结束后要释放连接。
是否是可靠传输：远地主机在收到UDP报文后，不需要给出任何确认，并
                        
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <!--
                            <i class="far fa-clock fa-fw icon-date"></i>2022-10-31
                            -->
                            
                                <i class="fas fa-user fa-fw"></i>
                                <a href="/blog/about" >
                                    xmxe
                                </a>
                            
                        </span>
                        <span class="publish-author">
                            
                                
                                <a href="/blog/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA/">
                                    <span class="chip bg-color">计算机</span>
                                </a>
                                
                            
                            
                        </span>
                    </div>
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>



<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/blog/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/blog/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/blog/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/blog/libs/codeBlock/codeShrink.js"></script>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/blog/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('0'),
            headingSelector: 'h1, h2, h3, h4, h5, h6'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h1, h2, h3, h4, h5, h6').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    
        <link rel="stylesheet" href="/blog/libs/aplayer/APlayer.min.css">
<style>
    .aplayer .aplayer-lrc p {
        
        display: none;
        
        font-size: 12px;
        font-weight: 700;
        line-height: 16px !important;
    }

    .aplayer .aplayer-lrc p.aplayer-lrc-current {
        
        display: none;
        
        font-size: 15px;
        color: #42b983;
    }

    
    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
        left: -66px !important;
    }

    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
        left: 0px !important;
    }

    
</style>
<div class="">
    
    <div class="row">
        <meting-js class="col l8 offset-l2 m10 offset-m1 s12"
                   server="netease"
                   type="song"
                   id="569200213"
                   fixed='true'
                   autoplay='false'
                   theme='#42b983'
                   loop='all'
                   order='random'
                   preload='auto'
                   volume='0.7'
                   list-folded='true'
        >
        </meting-js>
    </div>
</div>

<script src="/blog/libs/aplayer/APlayer.min.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script> -->

    
    <div class="container row center-align" style="margin-bottom: 0px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
            
                <span id="year">2022-2025
                </span>
            
            

            <a href="/blog/about" target="_blank">
                xmxe
            </a>
            |&nbsp;Powered by&nbsp;
            <a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;
            <a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a>
            <br>

            
            
            
            
            
            

            
            <br>

            
            <br>

            
        </div>
        <div class="col s12 m4 l4 social-link2 ">
    <a href="https://github.com/xmxe" class="tooltipped" target="_blank" data-tooltip="GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="https://gitee.com/xmxe" class="tooltipped" target="_blank" data-tooltip="码云" data-position="top" data-delay="50">
        <svg width="19" height="19" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" style="position: relative; top: 2px; left: 0.5px;">
            <path d="M512 1024C230.4 1024 0 793.6 0 512S230.4 0 512 0s512 230.4 512 512-230.4 512-512 512z m259.2-569.6H480c-12.8 0-25.6 12.8-25.6 25.6v64c0 12.8 12.8 25.6 25.6 25.6h176c12.8 0 25.6 12.8 25.6 25.6v12.8c0 41.6-35.2 76.8-76.8 76.8h-240c-12.8 0-25.6-12.8-25.6-25.6V416c0-41.6 35.2-76.8 76.8-76.8h355.2c12.8 0 25.6-12.8 25.6-25.6v-64c0-12.8-12.8-25.6-25.6-25.6H416c-105.6 0-188.8 86.4-188.8 188.8V768c0 12.8 12.8 25.6 25.6 25.6h374.4c92.8 0 169.6-76.8 169.6-169.6v-144c0-12.8-12.8-25.6-25.6-25.6z" fill="#fff">
            </path>
        </svg>
    </a>

















    
        
          <a href="/blog/download" class="tooltipped" target="_blank" data-tooltip="下载" data-position="top" data-delay="50">
            <i class="fas fa-download"></i>
          </a>
        
    



    <style>
  .mobiledevice {
    display: none !important;
  }

  footer .wechat_qrcode {
    position: fixed;
  }

  /*微信二维码*/
  .wechat_qrcode {
    position: absolute;
    margin-left: 10px;
    bottom: 10px;
    background: url("/blog/medias/xcx.png");
    zoom:40%;
  }

  .wechat:hover .wechat_qrcode {
    width: 430px;
    height: 430px;
    animation: move 0.4s linear 1 normal;
  }

  @keyframes move {
    0% {
      transform: translate(100px, 0);
      opacity: 0;
    }
    50% {
      transform: translate(50px, 0);
      opacity: 0.5;
    }
    100% {
      transform: translate(0, 0);
      opacity: 1;
    }
  }

  @media only screen and (max-width: 601px) {
    .wechat {
      display: none !important;
    }
    .mobiledevice {
      display: inline-block !important;
    }
  }
</style>

<a href="javascript:;" class="wechat" data-position="top" data-delay="50">
  <i class="fab fa-weixin"></i>
  <img class="wechat_qrcode" />
</a>

<a
  href="javascript:;"
  class="tooltipped mobiledevice"
  data-tooltip="微信: 464817304"
  data-position="top"
  data-delay="50"
>
  <i class="fab fa-weixin"></i>
</a>

</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
$(function () {
    var searchFunc = function (path, search_id, content_id) {
        'use strict';
        $.ajax({
            url: path,
            dataType: "xml",
            success: function (xmlResponse) {
                // get the contents from search data
                var datas = $("entry", xmlResponse).map(function () {
                    return {
                        title: $("title", this).text(),
                        content: $("content", this).text(),
                        url: $("url", this).text()
                    };
                }).get();
                var $input = document.getElementById(search_id);
                var $resultContent = document.getElementById(content_id);
                $input.addEventListener('input', function () {
                    var str = '<ul class=\"search-result-list\">';
                    var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                    $resultContent.innerHTML = "";
                    if (this.value.trim().length <= 0) {
                        return;
                    }
                    // perform local searching
                    datas.forEach(function (data) {
                        var isMatch = true;
                        var data_title = data.title.trim().toLowerCase();
                        var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                        var data_url = data.url;
                        data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                        var index_title = -1;
                        var index_content = -1;
                        var first_occur = -1;
                        // only match artiles with not empty titles and contents
                        if (data_title !== '' && data_content !== '') {
                            keywords.forEach(function (keyword, i) {
                                index_title = data_title.indexOf(keyword);
                                index_content = data_content.indexOf(keyword);
                                if (index_title < 0 && index_content < 0) {
                                    isMatch = false;
                                } else {
                                    if (index_content < 0) {
                                        index_content = 0;
                                    }
                                    if (i === 0) {
                                        first_occur = index_content;
                                    }
                                }
                            });
                        }
                        // show search results
                        if (isMatch) {
                            str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                            var content = data.content.trim().replace(/<[^>]+>/g, "");
                            if (first_occur >= 0) {
                                // cut out 100 characters
                                var start = first_occur - 20;
                                var end = first_occur + 80;
                                if (start < 0) {
                                    start = 0;
                                }
                                if (start === 0) {
                                    end = 100;
                                }
                                if (end > content.length) {
                                    end = content.length;
                                }
                                var match_content = content.substr(start, end);
                                // highlight all keywords
                                keywords.forEach(function (keyword) {
                                    var regS = new RegExp(keyword, "gi");
                                    match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                });

                                str += "<p class=\"search-result\">" + match_content + "...</p>"
                            }
                            str += "</li>";
                        }
                    });
                    str += "</ul>";
                    $resultContent.innerHTML = str;
                });
            }
        });
    };

    searchFunc('/blog/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>

    <div class="stars-con">
  <div id="stars"></div>
  <div id="stars2"></div>
  <div id="stars3"></div>  
</div>

<!-- 白天和黑夜主题 -->



<script>
  function switchNightMode() {
    
      setTimeout(function () {
        $('body').hasClass('DarkMode') 
        ? ($('body').removeClass('DarkMode'), localStorage.setItem('isDark', '0'), $('#sum-moon-icon').removeClass("fa-sun").addClass('fa-moon')) 
        : ($('body').addClass('DarkMode'), localStorage.setItem('isDark', '1'), $('#sum-moon-icon').addClass("fa-sun").removeClass('fa-moon')),
          
        setTimeout(function () {
          $('.Cuteen_DarkSky').fadeOut(1e3, function () {
            $(this).remove()
          })
        }, 2e3)
      })
  }
</script>
    
    
    <script>
        /* 模式判断 */
        if (localStorage.getItem('isDark') === '1') {
            document.body.classList.add('DarkMode');
            $('#sum-moon-icon').addClass("fa-sun").removeClass('fa-moon')
        } else {
            document.body.classList.remove('DarkMode');
            $('#sum-moon-icon').removeClass("fa-sun").addClass('fa-moon')
        }
    </script>

    <script src="/blog/libs/materialize/materialize.min.js"></script>
    <script src="/blog/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/blog/libs/aos/aos.js"></script>
    <script src="/blog/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/blog/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/blog/js/matery.js"></script>

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    

    

    

	
    

    

    
    <script type="text/javascript" src="/blog/libs/background/ribbon-dynamic.js" async="async"></script>
    

    

    <!-- 冒泡 -->
    
    <script type="text/javascript">
        // 只在桌面版网页启用特效
        // var windowWidth = $(window).width();
        
            document.write('<script type="text/javascript" src="/blog/libs/others/bubleAll.js"><\/script>');
        
        
    </script>
    

    <!-- 弹出文字 -->
    

</body>

</html>
